User prompt
Make the omega plant shoot if there is pest in any of the lanes
User prompt
Make the MegaRat spawn only regular pest instead of Partyrats
User prompt
If you finish all the levels then make so there is a extra 5 that has different plants and pest
User prompt
Well make so I can place a seed producer plant on top of a mega plant
User prompt
Make so that in order to make the omega plant you have to put a seed producer plant on top of a mega plant
User prompt
Make a new plant called omega plant it shoots in every lane the only way to make it is bye putting a seed producer plant on top of a mega plant. Also you can only make one also make a asset for it
User prompt
Make the mega rat rapidly spawn partyRats in all the lanes
User prompt
Make the max wave 20
User prompt
Make a new pest called the “mega rat” that has the most health in the whole game and instantly kills plants when he touches them it also is very big and takes up to lanes lastly make a asset for it
User prompt
Make the waves go up to 30
User prompt
Don’t make the mega plants bullets pierce
User prompt
I ment on the left of it
User prompt
Make my resources next to the seed shop
User prompt
Make the amount of seed bigger and black
User prompt
Move the seed shop to the bottom of the screen
User prompt
Put the price of each plant under there icon
User prompt
Make so you can only place a shooter on the trap
User prompt
Make so that if there is a trap plant on the one of the grids then you can place a plant on top of it to turn it into mega plant ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make so you can put the plant on top of the trap to make a mega plant ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make a secret plant called the mega plant. You can spawn it by putting a trap on a plant. also make it have a asset ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Can I make party rat’s spawn cooldown from 4 to 4.5 seconds
User prompt
Put the seed shop were it used to be
User prompt
Move the seed shop to the bottom right corner
User prompt
Call it the seed shop
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Fast, tough pest that spawns on wave 3 var FastPest = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('fastPest', { anchorX: 0.5, anchorY: 0.5, width: 120, height: 120 }); self.lane = null; self.hp = 5; // More health than basic pest self.speed = 4.5; // Much faster than basic pest self.destroyed = false; self.update = function () { if (self.destroyed) return; // Initialize lastX for transition checks if (self.lastX === undefined) self.lastX = self.x; // If currently eating a plant, handle eating logic if (self.eatingPlant && !self.eatingPlant._destroyed) { // Only bite every 30 frames if (self.eatCooldown === undefined) self.eatCooldown = 0; if (self._chewTweening !== true) { self._chewTweening = true; tween(self, { scaleX: 1.2 }, { duration: 80, yoyo: true, repeat: 2, onFinish: function onFinish() { self.scaleX = 1.0; self._chewTweening = false; } }); } if (self.eatCooldown > 0) { self.eatCooldown--; } else { // Damage the plant if (typeof self.eatingPlant.hp === "number") { self.eatingPlant.hp -= 1; // Optional: flash or animate plant here if (self.eatingPlant.hp <= 0) { self.eatingPlant._destroyed = true; if (typeof self.eatingPlant.destroy === "function") self.eatingPlant.destroy(); // Remove from plants array in main update self.eatingPlant = null; self.scaleX = 1.0; self._chewTweening = false; } } self.eatCooldown = 30; } // Don't move while eating } else { // Not eating, move left self.x -= self.speed; // Check for collision with plant in same lane var foundPlant = null; for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.lane === self.lane && !plant._destroyed && Math.abs(self.x - plant.x) < 60) { foundPlant = plant; break; } } if (foundPlant) { self.eatingPlant = foundPlant; self.eatCooldown = 0; } } // Update lastX self.lastX = self.x; }; self.hit = function (dmg) { if (self.destroyed) return; self.hp -= dmg; if (self.hp <= 0) { self.destroyed = true; } }; return self; }); // --- Pest Classes --- // Party Rat - high health pest that spawns other pests in 4 directions every 6 seconds var PartyRat = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('partyRat', { anchorX: 0.5, anchorY: 0.5, width: 140, height: 140 }); self.lane = null; self.hp = 15; // Very high health self.speed = 1.5; // Slower than other pests self.destroyed = false; self.spawnCooldown = 240; // 4 seconds at 60fps self.spawnTimer = 0; self.update = function () { if (self.destroyed) return; // Initialize lastX for transition checks if (self.lastX === undefined) self.lastX = self.x; // Handle spawning timer self.spawnTimer++; if (self.spawnTimer >= self.spawnCooldown) { self.spawnTimer = 0; self.spawnPests(); } // If currently eating a plant, handle eating logic if (self.eatingPlant && !self.eatingPlant._destroyed) { // Only bite every 30 frames if (self.eatCooldown === undefined) self.eatCooldown = 0; if (self._chewTweening !== true) { self._chewTweening = true; tween(self, { scaleX: 1.3 }, { duration: 100, yoyo: true, repeat: 2, onFinish: function onFinish() { self.scaleX = 1.0; self._chewTweening = false; } }); } if (self.eatCooldown > 0) { self.eatCooldown--; } else { // Damage the plant if (typeof self.eatingPlant.hp === "number") { self.eatingPlant.hp -= 2; // Party rat does more damage if (self.eatingPlant.hp <= 0) { self.eatingPlant._destroyed = true; if (typeof self.eatingPlant.destroy === "function") self.eatingPlant.destroy(); self.eatingPlant = null; self.scaleX = 1.0; self._chewTweening = false; } } self.eatCooldown = 30; } } else { // Not eating, move left self.x -= self.speed; // Check for collision with plant in same lane var foundPlant = null; for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.lane === self.lane && !plant._destroyed && Math.abs(self.x - plant.x) < 60) { foundPlant = plant; break; } } if (foundPlant) { self.eatingPlant = foundPlant; self.eatCooldown = 0; } } // Update lastX self.lastX = self.x; }; self.spawnPests = function () { // Spawn pests in 4 directions: front, back, up lane, down lane var spawnPositions = [{ x: self.x + 100, lane: self.lane }, // Front (right) { x: self.x - 100, lane: self.lane }, // Behind (left) { x: self.x, lane: Math.max(0, self.lane - 1) }, // Up lane { x: self.x, lane: Math.min(LANE_COUNT - 1, self.lane + 1) } // Down lane ]; for (var i = 0; i < spawnPositions.length; i++) { var pos = spawnPositions[i]; // Don't spawn if position is off-screen or invalid if (pos.x > 50 && pos.x < GAME_WIDTH - 50) { var newPest = new Pest(); newPest.lane = pos.lane; newPest.x = pos.x; newPest.y = LANE_Y[pos.lane]; pests.push(newPest); game.addChild(newPest); // Visual effect for spawning LK.effects.flashObject(newPest, 0xFF69B4, 300); } } // Visual effect on the party rat when it spawns LK.effects.flashObject(self, 0xFFFF00, 500); }; self.hit = function (dmg) { if (self.destroyed) return; self.hp -= dmg; if (self.hp <= 0) { self.destroyed = true; } }; return self; }); // Basic pest that moves left and can be hit by seeds var Pest = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('pest', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100 }); self.lane = null; self.hp = 2; self.speed = 2.0; self.destroyed = false; self.update = function () { if (self.destroyed) return; // Initialize lastX for transition checks if (self.lastX === undefined) self.lastX = self.x; // If currently eating a plant, handle eating logic if (self.eatingPlant && !self.eatingPlant._destroyed) { // Only bite every 30 frames if (self.eatCooldown === undefined) self.eatCooldown = 0; if (self._chewTweening !== true) { self._chewTweening = true; tween(self, { scaleX: 1.2 }, { duration: 80, yoyo: true, repeat: 2, onFinish: function onFinish() { self.scaleX = 1.0; self._chewTweening = false; } }); } if (self.eatCooldown > 0) { self.eatCooldown--; } else { // Damage the plant if (typeof self.eatingPlant.hp === "number") { self.eatingPlant.hp -= 1; // Optional: flash or animate plant here if (self.eatingPlant.hp <= 0) { self.eatingPlant._destroyed = true; if (typeof self.eatingPlant.destroy === "function") self.eatingPlant.destroy(); // Remove from plants array in main update self.eatingPlant = null; self.scaleX = 1.0; self._chewTweening = false; } } self.eatCooldown = 30; } // Don't move while eating } else { // Not eating, move left self.x -= self.speed; // Check for collision with plant in same lane var foundPlant = null; for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.lane === self.lane && !plant._destroyed && Math.abs(self.x - plant.x) < 60) { foundPlant = plant; break; } } if (foundPlant) { self.eatingPlant = foundPlant; self.eatCooldown = 0; } } // Update lastX self.lastX = self.x; }; self.hit = function (dmg) { if (self.destroyed) return; self.hp -= dmg; if (self.hp <= 0) { self.destroyed = true; } }; return self; }); // --- Plant Classes --- // Basic shooter plant var Plant = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('plant', { anchorX: 0.5, anchorY: 0.5, width: 120, height: 120 }); self.fireCooldown = 60; // frames between shots self.cooldown = 0; self.lane = null; self.column = null; self.hp = 5; // Plant health self._destroyed = false; self.update = function () { if (self.cooldown > 0) self.cooldown--; if (self.hp <= 0 && !self._destroyed) { self._destroyed = true; if (typeof self.destroy === "function") self.destroy(); } }; self.tryFire = function () { if (self.cooldown <= 0) { self.cooldown = self.fireCooldown; return true; } return false; }; return self; }); // --- Seed Class --- // Seed projectile fired by shooter plants var Seed = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('seed', { anchorX: 0.5, anchorY: 0.5, width: 40, height: 40 }); self.lane = null; self.speed = 10; self.update = function () { self.x += self.speed; }; return self; }); // Plant that generates resources var SeedProducerPlant = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('seedProducerPlant', { anchorX: 0.5, anchorY: 0.5, width: 120, height: 120 }); self.resourceCooldown = 90; // frames between resource generation (was 180, now 90 for faster seed gain) self.cooldown = 0; self.lane = null; self.column = null; self.hp = 5; // Plant health self._destroyed = false; self._seedsProduced = 0; self.update = function () { if (self.cooldown > 0) { self.cooldown--; } else { // Give resource and reset cooldown resources += 1; resourceTxt.setText(resources + ''); self.cooldown = self.resourceCooldown; self._seedsProduced = (self._seedsProduced || 0) + 1; if (self._seedsProduced % 2 === 0) { // Glow pink using tween (tint to pink, then back to normal) tween.stop(sprite, { tint: true }); var originalTint = sprite.tint; tween(sprite, { tint: 0xFF69B4 }, { duration: 120, yoyo: true, repeat: 1, onFinish: function onFinish() { sprite.tint = originalTint; } }); } } if (self.hp <= 0 && !self._destroyed) { self._destroyed = true; if (typeof self.destroy === "function") self.destroy(); } }; return self; }); // Trap plant that eats any pest that touches it, then disappears var TrapPlant = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('trapPlant', { anchorX: 0.5, anchorY: 0.5, width: 120, height: 120 }); self.lane = null; self.column = null; self.hp = 1; // Dies after eating one pest self._destroyed = false; self.hasEaten = false; self.cooldownTime = 6000; // 6 second cooldown in milliseconds self.cooldownRemaining = 0; self.onCooldown = false; self.update = function () { if (self._destroyed || self.hasEaten) return; // Handle cooldown if (self.onCooldown) { self.cooldownRemaining -= 16; // Approximately 16ms per frame at 60fps if (self.cooldownRemaining <= 0) { self.onCooldown = false; // Reset visual appearance when cooldown ends sprite.tint = 0xffffff; sprite.alpha = 1.0; } return; // Don't check for collisions while on cooldown } // Check for collision with any pest in same lane - improved collision detection for (var i = pests.length - 1; i >= 0; i--) { var pest = pests[i]; // Check if pest is in same lane and within touching distance if (pest.lane === self.lane && !pest.destroyed && Math.abs(self.x - pest.x) < 80 && Math.abs(self.y - pest.y) < 60) { // Instantly kill the pest - set HP to 0 and destroy immediately pest.hp = 0; pest.destroyed = true; pest.destroy(); // Remove from pests array immediately pests.splice(i, 1); // Start cooldown instead of destroying plant self.onCooldown = true; self.cooldownRemaining = self.cooldownTime; // Visual feedback for cooldown - make plant greyish and semi-transparent sprite.tint = 0x666666; sprite.alpha = 0.6; // Flash effect on successful kill LK.effects.flashObject(self, 0x00ff00, 200); break; } } if (self.hp <= 0 && !self._destroyed) { self._destroyed = true; if (typeof self.destroy === "function") self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x7ec850 // Light green garden }); /**** * Game Code ****/ // Add background image var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChild(background); // --- Constants --- // Unique sprite for fast, tough pest var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var LANE_COUNT = 7; var LANE_HEIGHT = Math.floor(GAME_HEIGHT / LANE_COUNT); var LANE_Y = []; for (var i = 0; i < LANE_COUNT; i++) { LANE_Y[i] = Math.floor((i + 0.5) * LANE_HEIGHT); } var PLANT_COST = 10; var START_RESOURCES = 20; var MAX_BREACHES = 5; var TOTAL_WAVES = 10; var COLUMN_COUNT = 5; // Number of plant columns var COLUMN_WIDTH = 160; // Distance between columns var COLUMN_START_X = 200; // X position of first column // --- Asset Initialization --- // --- Game State --- var plants = []; // All plant objects var pests = []; // All pest objects var seeds = []; // All seed objects var rockets = []; // All rocket objects (initialized in global game state) var resources = START_RESOURCES; var breaches = 0; var wave = 1; var waveInProgress = false; var waveTimer = 0; var nextPestTick = 0; var selectedLane = null; // Lane index for planting var selectedColumn = null; // Column index for planting var plantPreview = null; // Preview asset for planting // --- Lane Markers (for touch feedback) --- var laneMarkers = []; for (var i = 0; i < LANE_COUNT; i++) { for (var col = 0; col < COLUMN_COUNT; col++) { // Use a unique grid sprite for the grid cell background var marker = LK.getAsset('grid', { anchorX: 0.5, anchorY: 0.5, x: COLUMN_START_X + col * COLUMN_WIDTH, y: LANE_Y[i], alpha: 0.8, width: 180, height: LANE_HEIGHT - 10, tint: 0xD2B48C }); game.addChild(marker); laneMarkers.push(marker); } } // --- UI Elements --- // Plant selection UI var plantTypes = [{ id: 'plant', name: 'Shooter', asset: 'plant', desc: 'Shoots seeds', classRef: Plant }, { id: 'seedProducer', name: 'Producer', asset: 'seedProducerPlant', desc: 'Generates seeds', classRef: SeedProducerPlant }, { id: 'trapPlant', name: 'Trap', asset: 'trapPlant', desc: 'Eats one pest', classRef: TrapPlant }]; var selectedPlantType = plantTypes[0]; // Default to shooter var plantSelectButtons = []; var plantSelectY = 120; var plantSelectSpacing = 220; for (var i = 0; i < plantTypes.length; i++) { var btnX = 200 + i * plantSelectSpacing; var btn = LK.getAsset(plantTypes[i].asset, { anchorX: 0.5, anchorY: 0.5, x: btnX, y: plantSelectY, width: 120, height: 120, alpha: 0.9 }); btn._plantTypeIndex = i; btn.interactive = true; btn.buttonMode = true; // Add a border to show selection btn._border = LK.getAsset(plantTypes[i].asset, { anchorX: 0.5, anchorY: 0.5, x: btnX, y: plantSelectY, width: 140, height: 140, alpha: 0.0 // Will set to visible for selected }); LK.gui.top.addChild(btn._border); LK.gui.top.addChild(btn); plantSelectButtons.push(btn); // Add label var label = new Text2(plantTypes[i].name, { size: 40, fill: "#222" }); label.anchor.set(0.5, 0); label.x = btnX; label.y = plantSelectY + 70; LK.gui.top.addChild(label); } // Helper to update plant selection UI function updatePlantSelectUI() { for (var i = 0; i < plantSelectButtons.length; i++) { if (plantTypes[i] === selectedPlantType) { plantSelectButtons[i]._border.alpha = 0.7; plantSelectButtons[i]._border.tint = 0xFFD700; } else { plantSelectButtons[i]._border.alpha = 0.0; } } } updatePlantSelectUI(); // Add Seed Shop title var seedShopTitle = new Text2('Seed Shop', { size: 60, fill: "#333", font: "Impact" }); seedShopTitle.anchor.set(0.5, 0); seedShopTitle.x = GAME_WIDTH / 2; seedShopTitle.y = 40; LK.gui.top.addChild(seedShopTitle); // Add touch handler for plant selection for (var i = 0; i < plantSelectButtons.length; i++) { (function (idx) { plantSelectButtons[idx].down = function (x, y, obj) { selectedPlantType = plantTypes[idx]; updatePlantSelectUI(); // Hide preview if showing if (plantPreview) { plantPreview.destroy(); plantPreview = null; } showPlantPreview(null, null); selectedLane = null; selectedColumn = null; }; })(i); } var resourceTxt = new Text2(resources + '', { size: 90, fill: 0xFFF700 }); resourceTxt.anchor.set(0.5, 0); LK.gui.top.addChild(resourceTxt); var waveTxt = new Text2('Wave 1/' + TOTAL_WAVES, { size: 70, fill: 0xFFFFFF }); waveTxt.anchor.set(0.5, 0); LK.gui.top.addChild(waveTxt); var breachTxt = new Text2('Breaches: 0/' + MAX_BREACHES, { size: 70, fill: 0xFF4444 }); breachTxt.anchor.set(0.5, 0); LK.gui.top.addChild(breachTxt); // Position UI resourceTxt.x = 400; resourceTxt.y = 20; waveTxt.x = GAME_WIDTH / 2; waveTxt.y = 20; breachTxt.x = GAME_WIDTH - 400; breachTxt.y = 20; // --- Plant Preview (shows where plant will be placed) --- function showPlantPreview(lane, column) { if (plantPreview) { plantPreview.visible = false; } if (lane === null || column === null) return; if (plantPreview) { plantPreview.destroy(); plantPreview = null; } if (!plantPreview) { // Use correct asset for preview var assetId = selectedPlantType ? selectedPlantType.asset : 'plant'; plantPreview = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5, alpha: 0.5, width: 120, height: 120 }); game.addChild(plantPreview); } plantPreview.x = COLUMN_START_X + column * COLUMN_WIDTH; plantPreview.y = LANE_Y[lane]; plantPreview.visible = true; } // --- Plant Placement Handler --- function canPlacePlant(lane, column) { // Only one plant per lane-column position for (var i = 0; i < plants.length; i++) { if (plants[i].lane === lane && plants[i].column === column) return false; } return resources >= PLANT_COST; } function placePlant(lane, column) { if (!canPlacePlant(lane, column)) return false; var plant; if (selectedPlantType && selectedPlantType.classRef) { plant = new selectedPlantType.classRef(); } else { // fallback plant = new Plant(); } plant.lane = lane; plant.column = column; plant.x = COLUMN_START_X + column * COLUMN_WIDTH; plant.y = LANE_Y[lane]; plants.push(plant); game.addChild(plant); resources -= PLANT_COST; resourceTxt.setText(resources + ''); return true; } // --- Touch Handler for Planting --- game.down = function (x, y, obj) { // Ignore top 100px (menu area) if (y < 100) return; // Find lane var lane = Math.floor(y / LANE_HEIGHT); if (lane < 0 || lane >= LANE_COUNT) return; // Find column var column = Math.floor((x - COLUMN_START_X + COLUMN_WIDTH / 2) / COLUMN_WIDTH); if (column < 0 || column >= COLUMN_COUNT) return; selectedLane = lane; selectedColumn = column; showPlantPreview(lane, column); }; game.move = function (x, y, obj) { if (y < 100) { showPlantPreview(null); selectedLane = null; selectedColumn = null; return; } var lane = Math.floor(y / LANE_HEIGHT); var column = Math.floor((x - COLUMN_START_X + COLUMN_WIDTH / 2) / COLUMN_WIDTH); if (lane < 0 || lane >= LANE_COUNT || column < 0 || column >= COLUMN_COUNT) { showPlantPreview(null); selectedLane = null; selectedColumn = null; return; } selectedLane = lane; selectedColumn = column; showPlantPreview(lane, column); }; game.up = function (x, y, obj) { if (selectedLane !== null && selectedColumn !== null) { if (canPlacePlant(selectedLane, selectedColumn)) { placePlant(selectedLane, selectedColumn); } } showPlantPreview(null); selectedLane = null; selectedColumn = null; }; // --- Resource Generation --- var resourceTimer = LK.setInterval(function () { resources += 1; resourceTxt.setText(resources + ''); }, 500); // Increased resource gain rate (was 1000ms, now 500ms) // --- Wave System --- function startWave() { waveInProgress = true; waveTimer = 0; nextPestTick = 0; waveTxt.setText('Wave ' + wave + '/' + TOTAL_WAVES); // Show wave banner if (!game._waveBanner) { var banner = new Text2('Wave ' + wave, { size: 200, fill: 0xFFF700, font: "Impact", stroke: "#222", strokeThickness: 10 }); banner.anchor.set(0.5, 0.5); banner.x = GAME_WIDTH / 2; banner.y = GAME_HEIGHT / 2; banner.alpha = 0.0; game._waveBanner = banner; game.addChild(banner); } else { game._waveBanner.setText('Wave ' + wave); } game._waveBanner.alpha = 1.0; game._waveBanner.visible = true; // Fade out after 1.2s tween(game._waveBanner, { alpha: 0 }, { duration: 900, delay: 1200, onFinish: function onFinish() { game._waveBanner.visible = false; } }); } function endWave() { waveInProgress = false; wave++; if (wave > TOTAL_WAVES) { LK.showYouWin(); } else { // Short pause before next wave LK.setTimeout(function () { startWave(); }, 1800); } } // --- Pest Spawning --- function spawnPest() { var lane = Math.floor(Math.random() * LANE_COUNT); var pest; // Spawn different pest types based on wave if (wave >= 5 && wave <= 15 && Math.random() < 0.15) { // 15% chance to spawn PartyRat from wave 5-15 pest = new PartyRat(); } else if (wave >= 3 && wave <= 15) { pest = new FastPest(); } else { pest = new Pest(); } pest.lane = lane; pest.x = GAME_WIDTH - 100; pest.y = LANE_Y[lane]; pests.push(pest); game.addChild(pest); } // --- Main Game Update --- game.update = function () { // --- Wave logic --- if (!waveInProgress) { startWave(); return; } waveTimer++; // Pest spawn logic: spawn a pest every 60-90 ticks, up to 6+wave pests per wave if (nextPestTick <= 0) { if (waveTimer < (6 + wave) * 80) { spawnPest(); nextPestTick = 60 + Math.floor(Math.random() * 30); } } else { nextPestTick--; } // End wave if all pests spawned and none left if (waveTimer > (6 + wave) * 80 && pests.length === 0) { endWave(); return; } // --- Plants fire seeds --- for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; plant.update(); // Remove destroyed plants if (plant._destroyed) { plant.destroy(); plants.splice(i, 1); continue; } // Only shooter plants fire seeds if (typeof plant.tryFire === "function") { // Fire if pest in lane and cooldown ready var pestInLane = false; for (var j = 0; j < pests.length; j++) { if (pests[j].lane === plant.lane && pests[j].x > plant.x) { pestInLane = true; break; } } if (pestInLane && plant.tryFire()) { var seed = new Seed(); seed.x = plant.x + 60; seed.y = plant.y; seed.lane = plant.lane; seeds.push(seed); game.addChild(seed); } } } // --- Seeds move and hit pests --- for (var s = seeds.length - 1; s >= 0; s--) { var seed = seeds[s]; seed.update(); var hit = false; for (var p = 0; p < pests.length; p++) { var pest = pests[p]; if (pest.lane === seed.lane && !pest.destroyed && Math.abs(seed.x - pest.x) < 60) { pest.hit(1); hit = true; break; } } if (hit || seed.x > GAME_WIDTH + 50) { seed.destroy(); seeds.splice(s, 1); } } // --- Pests move and check for breach or death --- for (var p = pests.length - 1; p >= 0; p--) { var pest = pests[p]; if (pest.destroyed) { pest.destroy(); pests.splice(p, 1); continue; } pest.update(); if (pest.x < 80) { // Breach! breaches++; breachTxt.setText('Breaches: ' + breaches + '/' + MAX_BREACHES); LK.effects.flashScreen(0xff0000, 400); pest.destroy(); pests.splice(p, 1); if (breaches >= MAX_BREACHES) { LK.showGameOver(); return; } } } }; // --- Clean up on game over/win --- game.on('destroy', function () { LK.clearInterval(resourceTimer); plants = []; pests = []; seeds = []; breaches = 0; resources = START_RESOURCES; wave = 1; waveInProgress = false; if (plantPreview) { plantPreview.destroy(); plantPreview = null; } if (game._waveBanner) { game._waveBanner.visible = false; game._waveBanner.alpha = 0; } }); // --- Initial UI update --- resourceTxt.setText(resources + ''); waveTxt.setText('Wave 1/' + TOTAL_WAVES); breachTxt.setText('Breaches: 0/' + MAX_BREACHES); // --- Start background music --- LK.playMusic('Hi1234');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Fast, tough pest that spawns on wave 3
var FastPest = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('fastPest', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 120
});
self.lane = null;
self.hp = 5; // More health than basic pest
self.speed = 4.5; // Much faster than basic pest
self.destroyed = false;
self.update = function () {
if (self.destroyed) return;
// Initialize lastX for transition checks
if (self.lastX === undefined) self.lastX = self.x;
// If currently eating a plant, handle eating logic
if (self.eatingPlant && !self.eatingPlant._destroyed) {
// Only bite every 30 frames
if (self.eatCooldown === undefined) self.eatCooldown = 0;
if (self._chewTweening !== true) {
self._chewTweening = true;
tween(self, {
scaleX: 1.2
}, {
duration: 80,
yoyo: true,
repeat: 2,
onFinish: function onFinish() {
self.scaleX = 1.0;
self._chewTweening = false;
}
});
}
if (self.eatCooldown > 0) {
self.eatCooldown--;
} else {
// Damage the plant
if (typeof self.eatingPlant.hp === "number") {
self.eatingPlant.hp -= 1;
// Optional: flash or animate plant here
if (self.eatingPlant.hp <= 0) {
self.eatingPlant._destroyed = true;
if (typeof self.eatingPlant.destroy === "function") self.eatingPlant.destroy();
// Remove from plants array in main update
self.eatingPlant = null;
self.scaleX = 1.0;
self._chewTweening = false;
}
}
self.eatCooldown = 30;
}
// Don't move while eating
} else {
// Not eating, move left
self.x -= self.speed;
// Check for collision with plant in same lane
var foundPlant = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.lane === self.lane && !plant._destroyed && Math.abs(self.x - plant.x) < 60) {
foundPlant = plant;
break;
}
}
if (foundPlant) {
self.eatingPlant = foundPlant;
self.eatCooldown = 0;
}
}
// Update lastX
self.lastX = self.x;
};
self.hit = function (dmg) {
if (self.destroyed) return;
self.hp -= dmg;
if (self.hp <= 0) {
self.destroyed = true;
}
};
return self;
});
// --- Pest Classes ---
// Party Rat - high health pest that spawns other pests in 4 directions every 6 seconds
var PartyRat = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('partyRat', {
anchorX: 0.5,
anchorY: 0.5,
width: 140,
height: 140
});
self.lane = null;
self.hp = 15; // Very high health
self.speed = 1.5; // Slower than other pests
self.destroyed = false;
self.spawnCooldown = 240; // 4 seconds at 60fps
self.spawnTimer = 0;
self.update = function () {
if (self.destroyed) return;
// Initialize lastX for transition checks
if (self.lastX === undefined) self.lastX = self.x;
// Handle spawning timer
self.spawnTimer++;
if (self.spawnTimer >= self.spawnCooldown) {
self.spawnTimer = 0;
self.spawnPests();
}
// If currently eating a plant, handle eating logic
if (self.eatingPlant && !self.eatingPlant._destroyed) {
// Only bite every 30 frames
if (self.eatCooldown === undefined) self.eatCooldown = 0;
if (self._chewTweening !== true) {
self._chewTweening = true;
tween(self, {
scaleX: 1.3
}, {
duration: 100,
yoyo: true,
repeat: 2,
onFinish: function onFinish() {
self.scaleX = 1.0;
self._chewTweening = false;
}
});
}
if (self.eatCooldown > 0) {
self.eatCooldown--;
} else {
// Damage the plant
if (typeof self.eatingPlant.hp === "number") {
self.eatingPlant.hp -= 2; // Party rat does more damage
if (self.eatingPlant.hp <= 0) {
self.eatingPlant._destroyed = true;
if (typeof self.eatingPlant.destroy === "function") self.eatingPlant.destroy();
self.eatingPlant = null;
self.scaleX = 1.0;
self._chewTweening = false;
}
}
self.eatCooldown = 30;
}
} else {
// Not eating, move left
self.x -= self.speed;
// Check for collision with plant in same lane
var foundPlant = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.lane === self.lane && !plant._destroyed && Math.abs(self.x - plant.x) < 60) {
foundPlant = plant;
break;
}
}
if (foundPlant) {
self.eatingPlant = foundPlant;
self.eatCooldown = 0;
}
}
// Update lastX
self.lastX = self.x;
};
self.spawnPests = function () {
// Spawn pests in 4 directions: front, back, up lane, down lane
var spawnPositions = [{
x: self.x + 100,
lane: self.lane
},
// Front (right)
{
x: self.x - 100,
lane: self.lane
},
// Behind (left)
{
x: self.x,
lane: Math.max(0, self.lane - 1)
},
// Up lane
{
x: self.x,
lane: Math.min(LANE_COUNT - 1, self.lane + 1)
} // Down lane
];
for (var i = 0; i < spawnPositions.length; i++) {
var pos = spawnPositions[i];
// Don't spawn if position is off-screen or invalid
if (pos.x > 50 && pos.x < GAME_WIDTH - 50) {
var newPest = new Pest();
newPest.lane = pos.lane;
newPest.x = pos.x;
newPest.y = LANE_Y[pos.lane];
pests.push(newPest);
game.addChild(newPest);
// Visual effect for spawning
LK.effects.flashObject(newPest, 0xFF69B4, 300);
}
}
// Visual effect on the party rat when it spawns
LK.effects.flashObject(self, 0xFFFF00, 500);
};
self.hit = function (dmg) {
if (self.destroyed) return;
self.hp -= dmg;
if (self.hp <= 0) {
self.destroyed = true;
}
};
return self;
});
// Basic pest that moves left and can be hit by seeds
var Pest = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('pest', {
anchorX: 0.5,
anchorY: 0.5,
width: 100,
height: 100
});
self.lane = null;
self.hp = 2;
self.speed = 2.0;
self.destroyed = false;
self.update = function () {
if (self.destroyed) return;
// Initialize lastX for transition checks
if (self.lastX === undefined) self.lastX = self.x;
// If currently eating a plant, handle eating logic
if (self.eatingPlant && !self.eatingPlant._destroyed) {
// Only bite every 30 frames
if (self.eatCooldown === undefined) self.eatCooldown = 0;
if (self._chewTweening !== true) {
self._chewTweening = true;
tween(self, {
scaleX: 1.2
}, {
duration: 80,
yoyo: true,
repeat: 2,
onFinish: function onFinish() {
self.scaleX = 1.0;
self._chewTweening = false;
}
});
}
if (self.eatCooldown > 0) {
self.eatCooldown--;
} else {
// Damage the plant
if (typeof self.eatingPlant.hp === "number") {
self.eatingPlant.hp -= 1;
// Optional: flash or animate plant here
if (self.eatingPlant.hp <= 0) {
self.eatingPlant._destroyed = true;
if (typeof self.eatingPlant.destroy === "function") self.eatingPlant.destroy();
// Remove from plants array in main update
self.eatingPlant = null;
self.scaleX = 1.0;
self._chewTweening = false;
}
}
self.eatCooldown = 30;
}
// Don't move while eating
} else {
// Not eating, move left
self.x -= self.speed;
// Check for collision with plant in same lane
var foundPlant = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.lane === self.lane && !plant._destroyed && Math.abs(self.x - plant.x) < 60) {
foundPlant = plant;
break;
}
}
if (foundPlant) {
self.eatingPlant = foundPlant;
self.eatCooldown = 0;
}
}
// Update lastX
self.lastX = self.x;
};
self.hit = function (dmg) {
if (self.destroyed) return;
self.hp -= dmg;
if (self.hp <= 0) {
self.destroyed = true;
}
};
return self;
});
// --- Plant Classes ---
// Basic shooter plant
var Plant = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('plant', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 120
});
self.fireCooldown = 60; // frames between shots
self.cooldown = 0;
self.lane = null;
self.column = null;
self.hp = 5; // Plant health
self._destroyed = false;
self.update = function () {
if (self.cooldown > 0) self.cooldown--;
if (self.hp <= 0 && !self._destroyed) {
self._destroyed = true;
if (typeof self.destroy === "function") self.destroy();
}
};
self.tryFire = function () {
if (self.cooldown <= 0) {
self.cooldown = self.fireCooldown;
return true;
}
return false;
};
return self;
});
// --- Seed Class ---
// Seed projectile fired by shooter plants
var Seed = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('seed', {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
self.lane = null;
self.speed = 10;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Plant that generates resources
var SeedProducerPlant = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('seedProducerPlant', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 120
});
self.resourceCooldown = 90; // frames between resource generation (was 180, now 90 for faster seed gain)
self.cooldown = 0;
self.lane = null;
self.column = null;
self.hp = 5; // Plant health
self._destroyed = false;
self._seedsProduced = 0;
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
} else {
// Give resource and reset cooldown
resources += 1;
resourceTxt.setText(resources + '');
self.cooldown = self.resourceCooldown;
self._seedsProduced = (self._seedsProduced || 0) + 1;
if (self._seedsProduced % 2 === 0) {
// Glow pink using tween (tint to pink, then back to normal)
tween.stop(sprite, {
tint: true
});
var originalTint = sprite.tint;
tween(sprite, {
tint: 0xFF69B4
}, {
duration: 120,
yoyo: true,
repeat: 1,
onFinish: function onFinish() {
sprite.tint = originalTint;
}
});
}
}
if (self.hp <= 0 && !self._destroyed) {
self._destroyed = true;
if (typeof self.destroy === "function") self.destroy();
}
};
return self;
});
// Trap plant that eats any pest that touches it, then disappears
var TrapPlant = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('trapPlant', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 120
});
self.lane = null;
self.column = null;
self.hp = 1; // Dies after eating one pest
self._destroyed = false;
self.hasEaten = false;
self.cooldownTime = 6000; // 6 second cooldown in milliseconds
self.cooldownRemaining = 0;
self.onCooldown = false;
self.update = function () {
if (self._destroyed || self.hasEaten) return;
// Handle cooldown
if (self.onCooldown) {
self.cooldownRemaining -= 16; // Approximately 16ms per frame at 60fps
if (self.cooldownRemaining <= 0) {
self.onCooldown = false;
// Reset visual appearance when cooldown ends
sprite.tint = 0xffffff;
sprite.alpha = 1.0;
}
return; // Don't check for collisions while on cooldown
}
// Check for collision with any pest in same lane - improved collision detection
for (var i = pests.length - 1; i >= 0; i--) {
var pest = pests[i];
// Check if pest is in same lane and within touching distance
if (pest.lane === self.lane && !pest.destroyed && Math.abs(self.x - pest.x) < 80 && Math.abs(self.y - pest.y) < 60) {
// Instantly kill the pest - set HP to 0 and destroy immediately
pest.hp = 0;
pest.destroyed = true;
pest.destroy();
// Remove from pests array immediately
pests.splice(i, 1);
// Start cooldown instead of destroying plant
self.onCooldown = true;
self.cooldownRemaining = self.cooldownTime;
// Visual feedback for cooldown - make plant greyish and semi-transparent
sprite.tint = 0x666666;
sprite.alpha = 0.6;
// Flash effect on successful kill
LK.effects.flashObject(self, 0x00ff00, 200);
break;
}
}
if (self.hp <= 0 && !self._destroyed) {
self._destroyed = true;
if (typeof self.destroy === "function") self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x7ec850 // Light green garden
});
/****
* Game Code
****/
// Add background image
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(background);
// --- Constants ---
// Unique sprite for fast, tough pest
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var LANE_COUNT = 7;
var LANE_HEIGHT = Math.floor(GAME_HEIGHT / LANE_COUNT);
var LANE_Y = [];
for (var i = 0; i < LANE_COUNT; i++) {
LANE_Y[i] = Math.floor((i + 0.5) * LANE_HEIGHT);
}
var PLANT_COST = 10;
var START_RESOURCES = 20;
var MAX_BREACHES = 5;
var TOTAL_WAVES = 10;
var COLUMN_COUNT = 5; // Number of plant columns
var COLUMN_WIDTH = 160; // Distance between columns
var COLUMN_START_X = 200; // X position of first column
// --- Asset Initialization ---
// --- Game State ---
var plants = []; // All plant objects
var pests = []; // All pest objects
var seeds = []; // All seed objects
var rockets = []; // All rocket objects (initialized in global game state)
var resources = START_RESOURCES;
var breaches = 0;
var wave = 1;
var waveInProgress = false;
var waveTimer = 0;
var nextPestTick = 0;
var selectedLane = null; // Lane index for planting
var selectedColumn = null; // Column index for planting
var plantPreview = null; // Preview asset for planting
// --- Lane Markers (for touch feedback) ---
var laneMarkers = [];
for (var i = 0; i < LANE_COUNT; i++) {
for (var col = 0; col < COLUMN_COUNT; col++) {
// Use a unique grid sprite for the grid cell background
var marker = LK.getAsset('grid', {
anchorX: 0.5,
anchorY: 0.5,
x: COLUMN_START_X + col * COLUMN_WIDTH,
y: LANE_Y[i],
alpha: 0.8,
width: 180,
height: LANE_HEIGHT - 10,
tint: 0xD2B48C
});
game.addChild(marker);
laneMarkers.push(marker);
}
}
// --- UI Elements ---
// Plant selection UI
var plantTypes = [{
id: 'plant',
name: 'Shooter',
asset: 'plant',
desc: 'Shoots seeds',
classRef: Plant
}, {
id: 'seedProducer',
name: 'Producer',
asset: 'seedProducerPlant',
desc: 'Generates seeds',
classRef: SeedProducerPlant
}, {
id: 'trapPlant',
name: 'Trap',
asset: 'trapPlant',
desc: 'Eats one pest',
classRef: TrapPlant
}];
var selectedPlantType = plantTypes[0]; // Default to shooter
var plantSelectButtons = [];
var plantSelectY = 120;
var plantSelectSpacing = 220;
for (var i = 0; i < plantTypes.length; i++) {
var btnX = 200 + i * plantSelectSpacing;
var btn = LK.getAsset(plantTypes[i].asset, {
anchorX: 0.5,
anchorY: 0.5,
x: btnX,
y: plantSelectY,
width: 120,
height: 120,
alpha: 0.9
});
btn._plantTypeIndex = i;
btn.interactive = true;
btn.buttonMode = true;
// Add a border to show selection
btn._border = LK.getAsset(plantTypes[i].asset, {
anchorX: 0.5,
anchorY: 0.5,
x: btnX,
y: plantSelectY,
width: 140,
height: 140,
alpha: 0.0 // Will set to visible for selected
});
LK.gui.top.addChild(btn._border);
LK.gui.top.addChild(btn);
plantSelectButtons.push(btn);
// Add label
var label = new Text2(plantTypes[i].name, {
size: 40,
fill: "#222"
});
label.anchor.set(0.5, 0);
label.x = btnX;
label.y = plantSelectY + 70;
LK.gui.top.addChild(label);
}
// Helper to update plant selection UI
function updatePlantSelectUI() {
for (var i = 0; i < plantSelectButtons.length; i++) {
if (plantTypes[i] === selectedPlantType) {
plantSelectButtons[i]._border.alpha = 0.7;
plantSelectButtons[i]._border.tint = 0xFFD700;
} else {
plantSelectButtons[i]._border.alpha = 0.0;
}
}
}
updatePlantSelectUI();
// Add Seed Shop title
var seedShopTitle = new Text2('Seed Shop', {
size: 60,
fill: "#333",
font: "Impact"
});
seedShopTitle.anchor.set(0.5, 0);
seedShopTitle.x = GAME_WIDTH / 2;
seedShopTitle.y = 40;
LK.gui.top.addChild(seedShopTitle);
// Add touch handler for plant selection
for (var i = 0; i < plantSelectButtons.length; i++) {
(function (idx) {
plantSelectButtons[idx].down = function (x, y, obj) {
selectedPlantType = plantTypes[idx];
updatePlantSelectUI();
// Hide preview if showing
if (plantPreview) {
plantPreview.destroy();
plantPreview = null;
}
showPlantPreview(null, null);
selectedLane = null;
selectedColumn = null;
};
})(i);
}
var resourceTxt = new Text2(resources + '', {
size: 90,
fill: 0xFFF700
});
resourceTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(resourceTxt);
var waveTxt = new Text2('Wave 1/' + TOTAL_WAVES, {
size: 70,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
var breachTxt = new Text2('Breaches: 0/' + MAX_BREACHES, {
size: 70,
fill: 0xFF4444
});
breachTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(breachTxt);
// Position UI
resourceTxt.x = 400;
resourceTxt.y = 20;
waveTxt.x = GAME_WIDTH / 2;
waveTxt.y = 20;
breachTxt.x = GAME_WIDTH - 400;
breachTxt.y = 20;
// --- Plant Preview (shows where plant will be placed) ---
function showPlantPreview(lane, column) {
if (plantPreview) {
plantPreview.visible = false;
}
if (lane === null || column === null) return;
if (plantPreview) {
plantPreview.destroy();
plantPreview = null;
}
if (!plantPreview) {
// Use correct asset for preview
var assetId = selectedPlantType ? selectedPlantType.asset : 'plant';
plantPreview = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5,
width: 120,
height: 120
});
game.addChild(plantPreview);
}
plantPreview.x = COLUMN_START_X + column * COLUMN_WIDTH;
plantPreview.y = LANE_Y[lane];
plantPreview.visible = true;
}
// --- Plant Placement Handler ---
function canPlacePlant(lane, column) {
// Only one plant per lane-column position
for (var i = 0; i < plants.length; i++) {
if (plants[i].lane === lane && plants[i].column === column) return false;
}
return resources >= PLANT_COST;
}
function placePlant(lane, column) {
if (!canPlacePlant(lane, column)) return false;
var plant;
if (selectedPlantType && selectedPlantType.classRef) {
plant = new selectedPlantType.classRef();
} else {
// fallback
plant = new Plant();
}
plant.lane = lane;
plant.column = column;
plant.x = COLUMN_START_X + column * COLUMN_WIDTH;
plant.y = LANE_Y[lane];
plants.push(plant);
game.addChild(plant);
resources -= PLANT_COST;
resourceTxt.setText(resources + '');
return true;
}
// --- Touch Handler for Planting ---
game.down = function (x, y, obj) {
// Ignore top 100px (menu area)
if (y < 100) return;
// Find lane
var lane = Math.floor(y / LANE_HEIGHT);
if (lane < 0 || lane >= LANE_COUNT) return;
// Find column
var column = Math.floor((x - COLUMN_START_X + COLUMN_WIDTH / 2) / COLUMN_WIDTH);
if (column < 0 || column >= COLUMN_COUNT) return;
selectedLane = lane;
selectedColumn = column;
showPlantPreview(lane, column);
};
game.move = function (x, y, obj) {
if (y < 100) {
showPlantPreview(null);
selectedLane = null;
selectedColumn = null;
return;
}
var lane = Math.floor(y / LANE_HEIGHT);
var column = Math.floor((x - COLUMN_START_X + COLUMN_WIDTH / 2) / COLUMN_WIDTH);
if (lane < 0 || lane >= LANE_COUNT || column < 0 || column >= COLUMN_COUNT) {
showPlantPreview(null);
selectedLane = null;
selectedColumn = null;
return;
}
selectedLane = lane;
selectedColumn = column;
showPlantPreview(lane, column);
};
game.up = function (x, y, obj) {
if (selectedLane !== null && selectedColumn !== null) {
if (canPlacePlant(selectedLane, selectedColumn)) {
placePlant(selectedLane, selectedColumn);
}
}
showPlantPreview(null);
selectedLane = null;
selectedColumn = null;
};
// --- Resource Generation ---
var resourceTimer = LK.setInterval(function () {
resources += 1;
resourceTxt.setText(resources + '');
}, 500); // Increased resource gain rate (was 1000ms, now 500ms)
// --- Wave System ---
function startWave() {
waveInProgress = true;
waveTimer = 0;
nextPestTick = 0;
waveTxt.setText('Wave ' + wave + '/' + TOTAL_WAVES);
// Show wave banner
if (!game._waveBanner) {
var banner = new Text2('Wave ' + wave, {
size: 200,
fill: 0xFFF700,
font: "Impact",
stroke: "#222",
strokeThickness: 10
});
banner.anchor.set(0.5, 0.5);
banner.x = GAME_WIDTH / 2;
banner.y = GAME_HEIGHT / 2;
banner.alpha = 0.0;
game._waveBanner = banner;
game.addChild(banner);
} else {
game._waveBanner.setText('Wave ' + wave);
}
game._waveBanner.alpha = 1.0;
game._waveBanner.visible = true;
// Fade out after 1.2s
tween(game._waveBanner, {
alpha: 0
}, {
duration: 900,
delay: 1200,
onFinish: function onFinish() {
game._waveBanner.visible = false;
}
});
}
function endWave() {
waveInProgress = false;
wave++;
if (wave > TOTAL_WAVES) {
LK.showYouWin();
} else {
// Short pause before next wave
LK.setTimeout(function () {
startWave();
}, 1800);
}
}
// --- Pest Spawning ---
function spawnPest() {
var lane = Math.floor(Math.random() * LANE_COUNT);
var pest;
// Spawn different pest types based on wave
if (wave >= 5 && wave <= 15 && Math.random() < 0.15) {
// 15% chance to spawn PartyRat from wave 5-15
pest = new PartyRat();
} else if (wave >= 3 && wave <= 15) {
pest = new FastPest();
} else {
pest = new Pest();
}
pest.lane = lane;
pest.x = GAME_WIDTH - 100;
pest.y = LANE_Y[lane];
pests.push(pest);
game.addChild(pest);
}
// --- Main Game Update ---
game.update = function () {
// --- Wave logic ---
if (!waveInProgress) {
startWave();
return;
}
waveTimer++;
// Pest spawn logic: spawn a pest every 60-90 ticks, up to 6+wave pests per wave
if (nextPestTick <= 0) {
if (waveTimer < (6 + wave) * 80) {
spawnPest();
nextPestTick = 60 + Math.floor(Math.random() * 30);
}
} else {
nextPestTick--;
}
// End wave if all pests spawned and none left
if (waveTimer > (6 + wave) * 80 && pests.length === 0) {
endWave();
return;
}
// --- Plants fire seeds ---
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
plant.update();
// Remove destroyed plants
if (plant._destroyed) {
plant.destroy();
plants.splice(i, 1);
continue;
}
// Only shooter plants fire seeds
if (typeof plant.tryFire === "function") {
// Fire if pest in lane and cooldown ready
var pestInLane = false;
for (var j = 0; j < pests.length; j++) {
if (pests[j].lane === plant.lane && pests[j].x > plant.x) {
pestInLane = true;
break;
}
}
if (pestInLane && plant.tryFire()) {
var seed = new Seed();
seed.x = plant.x + 60;
seed.y = plant.y;
seed.lane = plant.lane;
seeds.push(seed);
game.addChild(seed);
}
}
}
// --- Seeds move and hit pests ---
for (var s = seeds.length - 1; s >= 0; s--) {
var seed = seeds[s];
seed.update();
var hit = false;
for (var p = 0; p < pests.length; p++) {
var pest = pests[p];
if (pest.lane === seed.lane && !pest.destroyed && Math.abs(seed.x - pest.x) < 60) {
pest.hit(1);
hit = true;
break;
}
}
if (hit || seed.x > GAME_WIDTH + 50) {
seed.destroy();
seeds.splice(s, 1);
}
}
// --- Pests move and check for breach or death ---
for (var p = pests.length - 1; p >= 0; p--) {
var pest = pests[p];
if (pest.destroyed) {
pest.destroy();
pests.splice(p, 1);
continue;
}
pest.update();
if (pest.x < 80) {
// Breach!
breaches++;
breachTxt.setText('Breaches: ' + breaches + '/' + MAX_BREACHES);
LK.effects.flashScreen(0xff0000, 400);
pest.destroy();
pests.splice(p, 1);
if (breaches >= MAX_BREACHES) {
LK.showGameOver();
return;
}
}
}
};
// --- Clean up on game over/win ---
game.on('destroy', function () {
LK.clearInterval(resourceTimer);
plants = [];
pests = [];
seeds = [];
breaches = 0;
resources = START_RESOURCES;
wave = 1;
waveInProgress = false;
if (plantPreview) {
plantPreview.destroy();
plantPreview = null;
}
if (game._waveBanner) {
game._waveBanner.visible = false;
game._waveBanner.alpha = 0;
}
});
// --- Initial UI update ---
resourceTxt.setText(resources + '');
waveTxt.setText('Wave 1/' + TOTAL_WAVES);
breachTxt.setText('Breaches: 0/' + MAX_BREACHES);
// --- Start background music ---
LK.playMusic('Hi1234');
Pixelated sun . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Flip it around
Make the background white
Make it angry
Make a dirty patch of dirt. In-Game asset. 2d. High contrast. No shadows
Make the background red
Make the background green
Make it shiny
Looks like a gargantuar from plants vs zombies but a rat. In-Game asset. 2d. High contrast. No shadows
Make the background blue