User prompt
Do background music according to the game
User prompt
Do harder when we complete waves one by one do hard
User prompt
Zombie was little bit powerful from shadowCreature.
User prompt
Add zombie
User prompt
Increase range to attack
User prompt
Add one more peashooterPod to buy . Base on add extra damge and health
User prompt
Add buy button for peashooterPod.
User prompt
Do give more peashooterPod. And opinion to buy to add in ground
User prompt
Enemy bar tell that how much enemy left
User prompt
When enemy damge all manasprout then then the game defeat
User prompt
Change the defeat system. When the all Tower down then the payer were defeat
User prompt
Fix the image
User prompt
Add background image according to the game and music
User prompt
When three defence Tower were dead then game over
User prompt
Make the hero to move with were on place
User prompt
The hero cannot move and attack to enemy with gun
Code edit (1 edits merged)
Please save this source code
User prompt
Mana Bloom: Guardian's Ascent
Initial prompt
let's make it a longer game idea that still holds onto the "no-lag" principle by focusing on clever content reuse and modular design. We'll expand the core concept, progression, and variety without adding excessive real-time processing demands. Mana Bloom: Guardian's Ascent (Extended No-Lag Edition) Concept: You are the last Mana Weaver, tasked with restoring the fragmented Great Mana Bloom across a series of corrupted garden realms. This is a 2D hybrid action-strategy game where you control a hero in small, fixed-screen arenas, planting simple, effective plant guardians to defend a vital "Mana Sprout" against waves of shadow creatures. The emphasis is on strategic decision-making, clever progression, and efficient visual design to ensure a smooth, lag-free experience across many hours of gameplay. Core Gameplay Loop: A Seamless Blend * Arena Combat (Action-Strategy): Each level is a self-contained, fixed-screen arena. Your hero character (e.g., "Seedling Scout," "Bloom Warden") moves freely, attacking incoming waves of shadow creatures with basic attacks. You collect "Sunlight Essences" dropped by defeated enemies, which are the primary resource for planting and upgrading your plant guardians. * Strategic Guardian Placement: From a limited hotbar, you tap to place different types of plant guardians (e.g., "Peashooter Pod" for single-target damage, "Sticky Vine" for slow, "Bursting Berry" for area damage). Guardians attack automatically. You'll need to learn enemy types and paths to optimize your defense. * Wave Management: Enemies arrive in distinct, timed waves. Between waves, there's a short breather to re-position your hero, collect any missed essences, and plan your next guardian placements or upgrades. Surviving all waves completes the arena. * Progression & Realm Exploration: Completing an arena unlocks the next, guiding you through a linear progression of increasingly challenging corrupted garden realms. Each realm introduces new enemy types, environmental hazards, and unique "Mana Sprout" challenges. Deep (Yet Optimized) Progression System * Hero Advancement: * Leveling & Core Stats: Your hero gains XP from defeating enemies and completing levels, increasing base stats like Health, Attack Damage, and Movement Speed. These are simple numerical increments, not complex calculations. * "Weaver Skills" (Passive Unlocks): Instead of an active skill tree, you unlock passive "Weaver Skills" at certain hero levels. These could be: * Essence Magnetism: Automatically collect nearby Sunlight Essences. * Rooted Resilience: Briefly gain defense when planting a guardian. * Sprout Vigor: Mana Sprout passively regenerates a small amount of health. * Efficient Planter: Reduces Sunlight cost for specific guardian types. * Artifacts (Equippable Boosts): Rarely dropped or earned, these are simple stat-boosting items (e.g., "Glimmering Dewdrop" for +5% Essence gain, "Tough Bark Vest" for +10 Health). Only 1-2 can be equipped at a time. * Guardian Evolution: * Unlocking New Types: As you progress through realms, you'll discover "Seed Pods" for new guardian types. Each new guardian offers a distinct strategic advantage. * Tier Upgrades: Each guardian type has 2-3 fixed tiers of upgrades (e.g., Peashooter Pod -> Spitting Pod -> Rapid-Fire Pod). Upgrading a guardian costs Sunlight Essences and boosts its damage, range, or adds a simple effect (e.g., a chance to pierce). These are pre-calculated values, not dynamic modifiers. * "Bloom Resonance" (Global Boosts): Completing certain challenges or realms grants "Bloom Resonance points." These points can be spent on global, passive buffs for all guardians (e.g., +2% overall guardian damage, +1% guardian attack speed). These are persistent and very small numerical boosts, easy for the engine to manage. Content Variety (Designed for No Lag) * Diverse Arenas (Fixed Screens): While each arena is fixed-screen, their layout changes significantly. Some might have winding paths, choke points, multiple Mana Sprouts, or environmental elements (e.g., "Slime Pits" that slow enemies, "Thorn Bushes" that damage them, "Fertile Patches" that boost guardian stats when planted there). These elements are static background layers, not active physics objects. * Unique Enemy Types: Each realm introduces a new set of shadow creatures. These have distinct appearances (simple sprites), movement patterns (pre-set paths), and attack types (melee, simple projectile). Examples: * "Skitterlings": Fast, low HP, rush the Mana Sprout. * "Gloom Beetles": Slow, high HP, require sustained fire. * "Shadow Wisps": Fly over ground units, only targetable by specific guardians or hero attacks. * "Corrupted Blight-Lord" (Bosses): Larger sprites, predictable attack patterns, higher health. These act as "gatekeepers" for new realms. * Thematic Realms: Each realm has a distinct visual theme using a simple palette swap and unique static background elements (e.g., "Sunken Grotto," "Whispering Woods," "Volcanic Fissure"). This creates variety without complex new assets per level. * Challenges & Objectives: Beyond just surviving waves, levels might have secondary objectives: * "Protect all 3 Mana Sprouts." * "Defeat X special enemies." * "Survive with no guardians planted." (Hero-only challenge) No-Lag Design Principles in Action * Minimal Asset Loading: Reuses guardian and enemy base sprites with simple color changes or minor additions for upgrades/variants. Environmental elements are static background layers. * No Physics Engine: All movement and collisions are handled via simple coordinate checks and predetermined paths. * Limited Particle Effects: Visuals for attacks are simple "hit sprites" or quick, small, non-persisting shapes, not complex particle systems. * Optimized Rendering: Uses a single render layer for most gameplay elements, reducing draw calls. * Efficient AI: Enemies follow pre-baked paths. Guardians target the nearest valid enemy. No complex decision-making processes. * Capped Entity Count: The game engine is designed to ensure a maximum, controlled number of enemies and projectiles are active on screen at any given time. * Low-Overhead Audio: Uses short, looping background music and compressed sound effects that are carefully managed to avoid overlapping too much. By focusing on these deliberate design choices, "Mana Bloom: Guardian's Ascent (Extended No-Lag Edition)" provides a rich, engaging, and long-lasting 2D experience that runs incredibly smoothly, proving that depth and longevity don't have to come at the cost of performance.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var HeavyPeashooterPod = Container.expand(function () { var self = Container.call(this); self.attackDamage = 35; // Higher damage than regular peashooter self.attackCooldown = 0; self.attackRate = 50; // Slightly slower attack rate self.range = 350; // Significantly longer range self.hp = 100; // Much higher health self.maxHp = 100; var podGraphics = self.attachAsset('peashooterPod', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, // Make it slightly larger to show it's heavy scaleY: 1.3, tint: 0x444444 // Darker tint to distinguish from regular }); self.takeDamage = function (damage) { self.hp -= damage; LK.effects.flashObject(self, 0xff0000, 300); if (self.hp <= 0) { self.hp = 0; towersDestroyed++; return true; // tower died } return false; }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } // Find enemy in range if (self.attackCooldown <= 0) { var targetEnemy = null; var closestDistance = self.range; for (var i = 0; i < shadowCreatures.length; i++) { var enemy = shadowCreatures[i]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance <= closestDistance) { closestDistance = distance; targetEnemy = enemy; } } for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2)); if (distance <= closestDistance) { closestDistance = distance; targetEnemy = zombie; } } if (targetEnemy) { // Shoot projectile var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.targetX = targetEnemy.x; projectile.targetY = targetEnemy.y; projectile.damage = self.attackDamage; game.addChild(projectile); projectiles.push(projectile); self.attackCooldown = self.attackRate; LK.getSound('shoot').play(); } } }; return self; }); var Hero = Container.expand(function () { var self = Container.call(this); self.hp = 100; self.maxHp = 100; self.speed = 4; self.attackDamage = 25; self.attackCooldown = 0; self.attackRate = 30; // frames between attacks self.targetX = 0; self.targetY = 0; self.isMoving = false; var heroGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.hp -= damage; if (self.hp <= 0) { self.hp = 0; LK.showGameOver(); } LK.effects.flashObject(self, 0xff0000, 300); }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } // Handle movement to target position if (self.isMoving) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Move towards target self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Reached target self.isMoving = false; } } // Gun attack - shoot projectiles at enemies if (self.attackCooldown <= 0) { var closestEnemy = null; var closestDistance = 400; // Gun range for (var i = 0; i < shadowCreatures.length; i++) { var enemy = shadowCreatures[i]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2)); if (distance < closestDistance) { closestDistance = distance; closestEnemy = zombie; } } if (closestEnemy) { // Shoot projectile at enemy var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.targetX = closestEnemy.x; projectile.targetY = closestEnemy.y; projectile.damage = self.attackDamage; game.addChild(projectile); projectiles.push(projectile); self.attackCooldown = self.attackRate; LK.getSound('shoot').play(); } } }; return self; }); var ManaSprout = Container.expand(function () { var self = Container.call(this); self.hp = 50; self.maxHp = 50; var sproutGraphics = self.attachAsset('manaSprout', { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.hp -= damage; if (self.hp <= 0) { self.hp = 0; manaSproutsDestroyed++; return true; // mana sprout died } LK.effects.flashObject(self, 0xff0000, 500); return false; }; return self; }); var PeashooterPod = Container.expand(function () { var self = Container.call(this); self.attackDamage = 20; self.attackCooldown = 0; self.attackRate = 45; self.range = 300; self.hp = 60; self.maxHp = 60; var podGraphics = self.attachAsset('peashooterPod', { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.hp -= damage; LK.effects.flashObject(self, 0xff0000, 300); if (self.hp <= 0) { self.hp = 0; towersDestroyed++; return true; // tower died } return false; }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } // Find enemy in range if (self.attackCooldown <= 0) { var targetEnemy = null; var closestDistance = self.range; for (var i = 0; i < shadowCreatures.length; i++) { var enemy = shadowCreatures[i]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance <= closestDistance) { closestDistance = distance; targetEnemy = enemy; } } for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2)); if (distance <= closestDistance) { closestDistance = distance; targetEnemy = zombie; } } if (targetEnemy) { // Shoot projectile var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.targetX = targetEnemy.x; projectile.targetY = targetEnemy.y; projectile.damage = self.attackDamage; game.addChild(projectile); projectiles.push(projectile); self.attackCooldown = self.attackRate; LK.getSound('shoot').play(); } } }; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); self.speed = 8; self.damage = 20; self.targetX = 0; self.targetY = 0; self.directionX = 0; self.directionY = 0; var projectileGraphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); // Calculate direction on creation self.setTarget = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.directionX = dx / distance; self.directionY = dy / distance; } }; self.update = function () { // Move towards target if (self.directionX === 0 && self.directionY === 0) { self.setTarget(self.targetX, self.targetY); } self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Check collision with shadow creatures for (var i = 0; i < shadowCreatures.length; i++) { var enemy = shadowCreatures[i]; if (self.intersects(enemy)) { if (enemy.takeDamage(self.damage)) { // Enemy died, remove it enemy.destroy(); shadowCreatures.splice(i, 1); } return true; // Projectile should be destroyed } } // Check collision with zombies for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (self.intersects(zombie)) { if (zombie.takeDamage(self.damage)) { // Zombie died, remove it zombie.destroy(); zombies.splice(i, 1); } return true; // Projectile should be destroyed } } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { return true; // Projectile should be destroyed } return false; }; return self; }); var ShadowCreature = Container.expand(function () { var self = Container.call(this); self.hp = 30; self.maxHp = 30; self.speed = 1; self.attackDamage = 10; self.attackCooldown = 0; self.attackRate = 60; self.target = null; self.lastX = 0; self.lastY = 0; var creatureGraphics = self.attachAsset('shadowCreature', { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.hp -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.hp <= 0) { var essence = new SunlightEssence(); essence.x = self.x; essence.y = self.y; game.addChild(essence); sunlightEssences.push(essence); LK.getSound('enemyHit').play(); return true; // enemy died } return false; }; self.update = function () { self.lastX = self.x; self.lastY = self.y; if (self.attackCooldown > 0) { self.attackCooldown--; } // Find closest target var closestDistance = Infinity; self.target = null; // Check mana sprouts for (var i = 0; i < manaSprouts.length; i++) { var sprout = manaSprouts[i]; var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2)); if (distance < closestDistance) { closestDistance = distance; self.target = sprout; } } // Check peashooter pods for (var i = 0; i < peashooterPods.length; i++) { var pod = peashooterPods[i]; var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2)); if (distance < closestDistance) { closestDistance = distance; self.target = pod; } } // Check heavy peashooter pods for (var i = 0; i < heavyPeashooterPods.length; i++) { var heavyPod = heavyPeashooterPods[i]; var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2)); if (distance < closestDistance) { closestDistance = distance; self.target = heavyPod; } } // Check hero if (hero) { var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2)); if (heroDistance < closestDistance) { closestDistance = heroDistance; self.target = hero; } } // Move towards target 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 > 40) { // Move towards target self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Attack target if (self.attackCooldown <= 0) { self.target.takeDamage(self.attackDamage); self.attackCooldown = self.attackRate; } } } }; return self; }); var SunlightEssence = Container.expand(function () { var self = Container.call(this); self.value = 10; var essenceGraphics = self.attachAsset('sunlightEssence', { anchorX: 0.5, anchorY: 0.5 }); // Add a gentle floating animation tween(self, { y: self.y - 10 }, { duration: 1000, easing: tween.easeInOut }); return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); self.hp = 80; self.maxHp = 80; self.speed = 0.5; // Slower than shadow creatures self.attackDamage = 15; self.attackCooldown = 0; self.attackRate = 90; // Slower attack rate self.target = null; self.lastX = 0; self.lastY = 0; var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.hp -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.hp <= 0) { var essence = new SunlightEssence(); essence.x = self.x; essence.y = self.y; game.addChild(essence); sunlightEssences.push(essence); LK.getSound('enemyHit').play(); return true; // enemy died } return false; }; self.update = function () { self.lastX = self.x; self.lastY = self.y; if (self.attackCooldown > 0) { self.attackCooldown--; } // Find closest target var closestDistance = Infinity; self.target = null; // Check mana sprouts for (var i = 0; i < manaSprouts.length; i++) { var sprout = manaSprouts[i]; var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2)); if (distance < closestDistance) { closestDistance = distance; self.target = sprout; } } // Check peashooter pods for (var i = 0; i < peashooterPods.length; i++) { var pod = peashooterPods[i]; var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2)); if (distance < closestDistance) { closestDistance = distance; self.target = pod; } } // Check heavy peashooter pods for (var i = 0; i < heavyPeashooterPods.length; i++) { var heavyPod = heavyPeashooterPods[i]; var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2)); if (distance < closestDistance) { closestDistance = distance; self.target = heavyPod; } } // Check hero if (hero) { var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2)); if (heroDistance < closestDistance) { closestDistance = heroDistance; self.target = hero; } } // Move towards target 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 > 40) { // Move towards target self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Attack target if (self.attackCooldown <= 0) { self.target.takeDamage(self.attackDamage); self.attackCooldown = self.attackRate; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d5016 }); /**** * Game Code ****/ // Game state variables var hero; var manaSprouts = []; var shadowCreatures = []; var zombies = []; var peashooterPods = []; var heavyPeashooterPods = []; var projectiles = []; var sunlightEssences = []; var currentEssences = 50; // Starting essences var waveNumber = 1; var enemiesRemaining = 0; var waveActive = false; var timeBetweenWaves = 300; // frames var waveTimer = 0; var selectedPlantType = 'peashooter'; var plantCost = 25; var heavyPlantCost = 50; var towersDestroyed = 0; var manaSproutsDestroyed = 0; // UI Elements var essenceText = new Text2('Essences: 50', { size: 60, fill: 0xFFFFFF }); essenceText.anchor.set(0, 0); essenceText.x = 120; essenceText.y = 50; LK.gui.topLeft.addChild(essenceText); var waveText = new Text2('Wave: 1', { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(1, 0); waveText.x = -50; waveText.y = 50; LK.gui.topRight.addChild(waveText); var statusText = new Text2('Prepare for battle!', { size: 80, fill: 0xFFFF00 }); statusText.anchor.set(0.5, 0); statusText.x = 0; statusText.y = 120; LK.gui.top.addChild(statusText); var enemyBarText = new Text2('Enemies: 0', { size: 60, fill: 0xFF4444 }); enemyBarText.anchor.set(0.5, 0); enemyBarText.x = 0; enemyBarText.y = 200; LK.gui.top.addChild(enemyBarText); var buyButton = new Text2('Buy Tower (25)', { size: 60, fill: 0x00FF00 }); buyButton.anchor.set(0, 1); buyButton.x = 120; buyButton.y = -50; LK.gui.bottomLeft.addChild(buyButton); var heavyBuyButton = new Text2('Buy Heavy (50)', { size: 60, fill: 0x00AA00 }); heavyBuyButton.anchor.set(0, 1); heavyBuyButton.x = 120; heavyBuyButton.y = -130; LK.gui.bottomLeft.addChild(heavyBuyButton); // Add background image var backgroundImage = game.addChild(LK.getAsset('gardenBackground', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 })); // Initialize hero hero = game.addChild(new Hero()); hero.x = 1024; hero.y = 1366; // Start background music LK.playMusic('gardenTheme'); // Initialize mana sprouts var sprout1 = game.addChild(new ManaSprout()); sprout1.x = 512; sprout1.y = 400; manaSprouts.push(sprout1); var sprout2 = game.addChild(new ManaSprout()); sprout2.x = 1536; sprout2.y = 400; manaSprouts.push(sprout2); var sprout3 = game.addChild(new ManaSprout()); sprout3.x = 1024; sprout3.y = 600; manaSprouts.push(sprout3); // Game state functions function startWave() { waveActive = true; enemiesRemaining = waveNumber * 3 + 2; statusText.setText('Wave ' + waveNumber + ' - Enemies: ' + enemiesRemaining); enemyBarText.setText('Enemies: ' + (enemiesRemaining + shadowCreatures.length)); // Spawn enemies gradually var enemySpawnTimer = LK.setInterval(function () { if (enemiesRemaining > 0) { spawnEnemy(); enemiesRemaining--; statusText.setText('Wave ' + waveNumber + ' - Enemies: ' + enemiesRemaining); enemyBarText.setText('Enemies: ' + (enemiesRemaining + shadowCreatures.length)); } }, 60); LK.setTimeout(function () { LK.clearInterval(enemySpawnTimer); }, enemiesRemaining * 60); } function spawnEnemy() { var enemy; // 30% chance to spawn zombie, 70% chance to spawn shadow creature if (Math.random() < 0.3) { enemy = new Zombie(); } else { enemy = new ShadowCreature(); } // Spawn from random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top enemy.x = Math.random() * 2048; enemy.y = -50; break; case 1: // Right enemy.x = 2098; enemy.y = Math.random() * 2732; break; case 2: // Bottom enemy.x = Math.random() * 2048; enemy.y = 2782; break; case 3: // Left enemy.x = -50; enemy.y = Math.random() * 2732; break; } game.addChild(enemy); if (enemy instanceof Zombie) { zombies.push(enemy); } else { shadowCreatures.push(enemy); } } function endWave() { waveActive = false; waveNumber++; waveTimer = timeBetweenWaves; waveText.setText('Wave: ' + waveNumber); statusText.setText('Wave Complete! Next wave in ' + Math.ceil(waveTimer / 60) + 's'); // Bonus essences for completing wave currentEssences += 20; updateEssenceDisplay(); } function updateEssenceDisplay() { essenceText.setText('Essences: ' + currentEssences); } function collectEssence(essence) { currentEssences += essence.value; updateEssenceDisplay(); LK.getSound('collect').play(); } // Input handling var dragNode = null; var plantingMode = false; // Buy button click handler buyButton.down = function (x, y, obj) { if (currentEssences >= plantCost && !waveActive) { selectedPlantType = 'peashooter'; plantingMode = true; buyButton.setText('Click to Plant!'); buyButton.fill = 0xFFFF00; heavyBuyButton.setText('Buy Heavy (50)'); heavyBuyButton.fill = 0x00AA00; } }; // Heavy buy button click handler heavyBuyButton.down = function (x, y, obj) { if (currentEssences >= heavyPlantCost && !waveActive) { selectedPlantType = 'heavy'; plantingMode = true; heavyBuyButton.setText('Click to Plant!'); heavyBuyButton.fill = 0xFFFF00; buyButton.setText('Buy Tower (25)'); buyButton.fill = 0x00FF00; } }; function handleMove(x, y, obj) { if (dragNode) { dragNode.x = x; dragNode.y = y; } } game.move = handleMove; game.down = function (x, y, obj) { // Set hero target position for movement hero.targetX = x; hero.targetY = y; hero.isMoving = true; // Check if in planting mode if (plantingMode && !waveActive) { var currentCost = selectedPlantType === 'heavy' ? heavyPlantCost : plantCost; if (currentEssences >= currentCost) { // Check if position is valid (not too close to existing plants or sprouts) var validPosition = true; for (var i = 0; i < peashooterPods.length; i++) { var pod = peashooterPods[i]; var distance = Math.sqrt(Math.pow(x - pod.x, 2) + Math.pow(y - pod.y, 2)); if (distance < 80) { validPosition = false; break; } } for (var i = 0; i < heavyPeashooterPods.length; i++) { var heavyPod = heavyPeashooterPods[i]; var distance = Math.sqrt(Math.pow(x - heavyPod.x, 2) + Math.pow(y - heavyPod.y, 2)); if (distance < 80) { validPosition = false; break; } } for (var i = 0; i < manaSprouts.length; i++) { var sprout = manaSprouts[i]; var distance = Math.sqrt(Math.pow(x - sprout.x, 2) + Math.pow(y - sprout.y, 2)); if (distance < 80) { validPosition = false; break; } } if (validPosition) { if (selectedPlantType === 'heavy') { var newHeavyPod = new HeavyPeashooterPod(); newHeavyPod.x = x; newHeavyPod.y = y; game.addChild(newHeavyPod); heavyPeashooterPods.push(newHeavyPod); currentEssences -= heavyPlantCost; } else { var newPod = new PeashooterPod(); newPod.x = x; newPod.y = y; game.addChild(newPod); peashooterPods.push(newPod); currentEssences -= plantCost; } updateEssenceDisplay(); LK.getSound('plant').play(); plantingMode = false; buyButton.setText('Buy Tower (25)'); buyButton.fill = 0x00FF00; heavyBuyButton.setText('Buy Heavy (50)'); heavyBuyButton.fill = 0x00AA00; } } } }; game.up = function (x, y, obj) { dragNode = null; }; // Start first wave after delay waveTimer = 180; // 3 seconds game.update = function () { // Handle wave timing if (!waveActive) { if (waveTimer > 0) { waveTimer--; if (waveTimer % 60 === 0) { statusText.setText('Next wave in ' + Math.ceil(waveTimer / 60) + 's'); } } else { startWave(); } } else { // Check if wave is complete if (enemiesRemaining <= 0 && shadowCreatures.length === 0 && zombies.length === 0) { endWave(); } } // Update enemy bar display var totalEnemies = enemiesRemaining + shadowCreatures.length + zombies.length; enemyBarText.setText('Enemies: ' + totalEnemies); // Update buy button availability if (!plantingMode) { if (waveActive) { buyButton.setText('Buy Tower (Wave Active)'); buyButton.fill = 0xFF0000; heavyBuyButton.setText('Buy Heavy (Wave Active)'); heavyBuyButton.fill = 0xFF0000; } else if (currentEssences < plantCost) { buyButton.setText('Buy Tower (Need ' + plantCost + ')'); buyButton.fill = 0xFF0000; if (currentEssences < heavyPlantCost) { heavyBuyButton.setText('Buy Heavy (Need ' + heavyPlantCost + ')'); heavyBuyButton.fill = 0xFF0000; } else { heavyBuyButton.setText('Buy Heavy (50)'); heavyBuyButton.fill = 0x00AA00; } } else { buyButton.setText('Buy Tower (25)'); buyButton.fill = 0x00FF00; if (currentEssences < heavyPlantCost) { heavyBuyButton.setText('Buy Heavy (Need ' + heavyPlantCost + ')'); heavyBuyButton.fill = 0xFF0000; } else { heavyBuyButton.setText('Buy Heavy (50)'); heavyBuyButton.fill = 0x00AA00; } } } // Update projectiles for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; var shouldDestroy = projectile.update(); if (shouldDestroy) { projectile.destroy(); projectiles.splice(i, 1); } } // Check for destroyed towers for (var i = peashooterPods.length - 1; i >= 0; i--) { var pod = peashooterPods[i]; if (pod.hp <= 0) { pod.destroy(); peashooterPods.splice(i, 1); } } // Check for destroyed heavy towers for (var i = heavyPeashooterPods.length - 1; i >= 0; i--) { var heavyPod = heavyPeashooterPods[i]; if (heavyPod.hp <= 0) { heavyPod.destroy(); heavyPeashooterPods.splice(i, 1); } } // Check for destroyed mana sprouts for (var i = manaSprouts.length - 1; i >= 0; i--) { var sprout = manaSprouts[i]; if (sprout.hp <= 0) { sprout.destroy(); manaSprouts.splice(i, 1); } } // Check game over condition - 3 towers destroyed if (towersDestroyed >= 3) { LK.showGameOver(); } // Check defeat condition - all mana sprouts destroyed if (manaSproutsDestroyed >= 3) { LK.showGameOver(); } // Check essence collection for (var i = sunlightEssences.length - 1; i >= 0; i--) { var essence = sunlightEssences[i]; var distance = Math.sqrt(Math.pow(hero.x - essence.x, 2) + Math.pow(hero.y - essence.y, 2)); if (distance < 40) { collectEssence(essence); essence.destroy(); sunlightEssences.splice(i, 1); } } // Hero attack now handled in Hero class update method // Check win condition if (waveNumber > 5 && !waveActive && shadowCreatures.length === 0 && zombies.length === 0) { LK.showYouWin(); } };
===================================================================
--- original.js
+++ change.js
@@ -38,26 +38,39 @@
self.attackCooldown--;
}
// Find enemy in range
if (self.attackCooldown <= 0) {
+ var targetEnemy = null;
+ var closestDistance = self.range;
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
- if (distance <= self.range) {
- // Shoot projectile
- var projectile = new Projectile();
- projectile.x = self.x;
- projectile.y = self.y;
- projectile.targetX = enemy.x;
- projectile.targetY = enemy.y;
- projectile.damage = self.attackDamage;
- game.addChild(projectile);
- projectiles.push(projectile);
- self.attackCooldown = self.attackRate;
- LK.getSound('shoot').play();
- break;
+ if (distance <= closestDistance) {
+ closestDistance = distance;
+ targetEnemy = enemy;
}
}
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
+ if (distance <= closestDistance) {
+ closestDistance = distance;
+ targetEnemy = zombie;
+ }
+ }
+ if (targetEnemy) {
+ // Shoot projectile
+ var projectile = new Projectile();
+ projectile.x = self.x;
+ projectile.y = self.y;
+ projectile.targetX = targetEnemy.x;
+ projectile.targetY = targetEnemy.y;
+ projectile.damage = self.attackDamage;
+ game.addChild(projectile);
+ projectiles.push(projectile);
+ self.attackCooldown = self.attackRate;
+ LK.getSound('shoot').play();
+ }
}
};
return self;
});
@@ -113,8 +126,16 @@
closestDistance = distance;
closestEnemy = enemy;
}
}
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestEnemy = zombie;
+ }
+ }
if (closestEnemy) {
// Shoot projectile at enemy
var projectile = new Projectile();
projectile.x = self.x;
@@ -178,26 +199,39 @@
self.attackCooldown--;
}
// Find enemy in range
if (self.attackCooldown <= 0) {
+ var targetEnemy = null;
+ var closestDistance = self.range;
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
- if (distance <= self.range) {
- // Shoot projectile
- var projectile = new Projectile();
- projectile.x = self.x;
- projectile.y = self.y;
- projectile.targetX = enemy.x;
- projectile.targetY = enemy.y;
- projectile.damage = self.attackDamage;
- game.addChild(projectile);
- projectiles.push(projectile);
- self.attackCooldown = self.attackRate;
- LK.getSound('shoot').play();
- break;
+ if (distance <= closestDistance) {
+ closestDistance = distance;
+ targetEnemy = enemy;
}
}
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
+ if (distance <= closestDistance) {
+ closestDistance = distance;
+ targetEnemy = zombie;
+ }
+ }
+ if (targetEnemy) {
+ // Shoot projectile
+ var projectile = new Projectile();
+ projectile.x = self.x;
+ projectile.y = self.y;
+ projectile.targetX = targetEnemy.x;
+ projectile.targetY = targetEnemy.y;
+ projectile.damage = self.attackDamage;
+ game.addChild(projectile);
+ projectiles.push(projectile);
+ self.attackCooldown = self.attackRate;
+ LK.getSound('shoot').play();
+ }
}
};
return self;
});
@@ -229,9 +263,9 @@
self.setTarget(self.targetX, self.targetY);
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
- // Check collision with enemies
+ // Check collision with shadow creatures
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
if (self.intersects(enemy)) {
if (enemy.takeDamage(self.damage)) {
@@ -241,8 +275,20 @@
}
return true; // Projectile should be destroyed
}
}
+ // Check collision with zombies
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ if (self.intersects(zombie)) {
+ if (zombie.takeDamage(self.damage)) {
+ // Zombie died, remove it
+ zombie.destroy();
+ zombies.splice(i, 1);
+ }
+ return true; // Projectile should be destroyed
+ }
+ }
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
return true; // Projectile should be destroyed
}
@@ -358,8 +404,101 @@
easing: tween.easeInOut
});
return self;
});
+var Zombie = Container.expand(function () {
+ var self = Container.call(this);
+ self.hp = 80;
+ self.maxHp = 80;
+ self.speed = 0.5; // Slower than shadow creatures
+ self.attackDamage = 15;
+ self.attackCooldown = 0;
+ self.attackRate = 90; // Slower attack rate
+ self.target = null;
+ self.lastX = 0;
+ self.lastY = 0;
+ var zombieGraphics = self.attachAsset('zombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.takeDamage = function (damage) {
+ self.hp -= damage;
+ LK.effects.flashObject(self, 0xffffff, 200);
+ if (self.hp <= 0) {
+ var essence = new SunlightEssence();
+ essence.x = self.x;
+ essence.y = self.y;
+ game.addChild(essence);
+ sunlightEssences.push(essence);
+ LK.getSound('enemyHit').play();
+ return true; // enemy died
+ }
+ return false;
+ };
+ self.update = function () {
+ self.lastX = self.x;
+ self.lastY = self.y;
+ if (self.attackCooldown > 0) {
+ self.attackCooldown--;
+ }
+ // Find closest target
+ var closestDistance = Infinity;
+ self.target = null;
+ // Check mana sprouts
+ for (var i = 0; i < manaSprouts.length; i++) {
+ var sprout = manaSprouts[i];
+ var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2));
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ self.target = sprout;
+ }
+ }
+ // Check peashooter pods
+ for (var i = 0; i < peashooterPods.length; i++) {
+ var pod = peashooterPods[i];
+ var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2));
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ self.target = pod;
+ }
+ }
+ // Check heavy peashooter pods
+ for (var i = 0; i < heavyPeashooterPods.length; i++) {
+ var heavyPod = heavyPeashooterPods[i];
+ var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2));
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ self.target = heavyPod;
+ }
+ }
+ // Check hero
+ if (hero) {
+ var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
+ if (heroDistance < closestDistance) {
+ closestDistance = heroDistance;
+ self.target = hero;
+ }
+ }
+ // Move towards target
+ 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 > 40) {
+ // Move towards target
+ self.x += dx / distance * self.speed;
+ self.y += dy / distance * self.speed;
+ } else {
+ // Attack target
+ if (self.attackCooldown <= 0) {
+ self.target.takeDamage(self.attackDamage);
+ self.attackCooldown = self.attackRate;
+ }
+ }
+ }
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -373,8 +512,9 @@
// Game state variables
var hero;
var manaSprouts = [];
var shadowCreatures = [];
+var zombies = [];
var peashooterPods = [];
var heavyPeashooterPods = [];
var projectiles = [];
var sunlightEssences = [];
@@ -485,9 +625,15 @@
LK.clearInterval(enemySpawnTimer);
}, enemiesRemaining * 60);
}
function spawnEnemy() {
- var enemy = new ShadowCreature();
+ var enemy;
+ // 30% chance to spawn zombie, 70% chance to spawn shadow creature
+ if (Math.random() < 0.3) {
+ enemy = new Zombie();
+ } else {
+ enemy = new ShadowCreature();
+ }
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
@@ -511,9 +657,13 @@
enemy.y = Math.random() * 2732;
break;
}
game.addChild(enemy);
- shadowCreatures.push(enemy);
+ if (enemy instanceof Zombie) {
+ zombies.push(enemy);
+ } else {
+ shadowCreatures.push(enemy);
+ }
}
function endWave() {
waveActive = false;
waveNumber++;
@@ -643,14 +793,14 @@
startWave();
}
} else {
// Check if wave is complete
- if (enemiesRemaining <= 0 && shadowCreatures.length === 0) {
+ if (enemiesRemaining <= 0 && shadowCreatures.length === 0 && zombies.length === 0) {
endWave();
}
}
// Update enemy bar display
- var totalEnemies = enemiesRemaining + shadowCreatures.length;
+ var totalEnemies = enemiesRemaining + shadowCreatures.length + zombies.length;
enemyBarText.setText('Enemies: ' + totalEnemies);
// Update buy button availability
if (!plantingMode) {
if (waveActive) {
@@ -732,8 +882,8 @@
}
}
// Hero attack now handled in Hero class update method
// Check win condition
- if (waveNumber > 5 && !waveActive && shadowCreatures.length === 0) {
+ if (waveNumber > 5 && !waveActive && shadowCreatures.length === 0 && zombies.length === 0) {
LK.showYouWin();
}
};
\ No newline at end of file
Hero. In-Game asset. 2d. High contrast. No shadows
manaSprout. In-Game asset. 2d. High contrast. No shadows
sunlightEssence. In-Game asset. 2d. High contrast. No shadows
projectile. In-Game asset. 2d. High contrast. No shadows
peashooterPod. In-Game asset. 2d. High contrast. No shadows
shadowCreature. In-Game asset. 2d. High contrast. No shadows
Makeup green colour background in the background and grass. In-Game asset. 2d. High contrast. No shadows
zombie. In-Game asset. 2d. High contrast. No shadows