User prompt
añade una nueva planta, será la repetidora, funcionará exactamente igual que el lanzaguisantes normal, pero costará 200 soles, ademas, de que al disparar lanza 2 guisantes, el 2 lo lanza 0,3 segundos despues del primero ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que el zombidito aparezca cuando: el zombistein tenga menos de 1500 puntos de vida, el zombiestein se quedará totalmente quieto durante 1,5 segundos, tras ese periodo volverá a moverse y 3 casillas enfrente de donde estaba el zombiestein aparecerá el zombidito con una explosion, la explosion será como la del asset de la explosion del minero ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
súbele la vida al zombistein de 1000 hp a 3000 hp, tambien hazlo un 15% mas rapido que el resto de zombies. Crea al "zombidito" este tendrá 250 puntos de vida y que haga 50 de daño por segundo. este unica y exclusivamente aparece en la oleada 10 y aparece de la siguiente forma: cuando el zombistein tenga menos de 1500 puntos de vida, el zombiestein se quedará totalmente quieto durante 1,5 segundos, tras ese periodo volverá a moverse y 3 casillas enfrente de donde estaba el zombiestein aparecerá el zombidito con una explosion, la explosion será como la del asset de la explosion del minero ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que en vez de 50% sea del 100%. tambien vamos a hacer al zombistein, este tiene 1000 puntos de vida y causa 500 de daño por segundo, solo aparece 1 zombistein por partida, aparece en la oleada 10 junto a todos los demas zombies, y aunque mates a todos los zombies de dicha oleada si aun no has matado al zombistein no puedes ganar la partida. tambien quiero que añadas un nuevo tipo de zombie, el zombidito, pero este te lo explicaré mas adelante.
User prompt
haz que apartir de la 6 oleada haya un 50% de probabilidad de que aparezca el zombie minero, pero solo puede aparecer un maximo de 1 minero por oleada, es decir, no puede haber mas de 1 minero en una misma oleada, sin embargo, a partir de la 8 oleada el maximo aumenta a un 2.
User prompt
Optimiza el juego para que no haya lag a partir de la oleada 6. Cambia la lógica de los proyectiles para que: Solo revisen colisiones con zombis de la misma fila (gridY) y cercanos (máx. 100px de distancia en el eje X). Cada planta tenga como máximo 2 proyectiles activos al mismo tiempo. Si es posible, reutiliza proyectiles eliminados en lugar de crear objetos nuevos cada vez (object pooling). Mantén la jugabilidad igual pero reduciendo el número de comprobaciones por frame y creaciones/destrucciones de objetos.
User prompt
el juego tiene mucho lag debido a la cantidad de zombies a partir de la oleada 6, quiero que a partir de ahora hagas que a partir de la oleada 6 aparecen un 50% menos de zombies de los que aparecen ahora, pero que a cambio, tengan un 25% mas de probabilidad de ser zombies conos o zombies cubo
User prompt
quiero que el juego sea más lento, todo ocurre demasiado rapido, no da tiempo a pensar que hacer, debes de hacer todos los aspectos del juego un 20% por ciento mas lento, esto incluye; la aparicion de soles de plantas y cielo, el countdown de disparos del lanzaguisantes, la velocidad de los zombies, etc... ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
it dont work, delete the spawning logic of the waves 1 and 2, and set the spawn logic of the waves 1 and 2 to:1 wave: 3-4 normal zombies. 2 wave: 3-4 normal zombies 75% zombie sun 1 zombie cone.
User prompt
haz que la aparicion de zombies sea 1 oleada: 3-4 zombies normales. 2 oleada: 3-4 zombies normales 75% de zombie sol 1 zombie cono. elimina el patron de aparicion oleada anterior de las oleadas 1 y 2
User prompt
añade un nuevo tipo de zombie, el zombie minero. añade otro nuevo tipo de zombie, el zombiestein. El minero será igual que un zombie normal, pero, cuando aparece, aparece directamente en la quinta cuadricula del mapa y aparece un asset de una explosion sobre él cuando aparece. la unica diferencia con un zombie normal aparte de esa mecanica es que tiene un asset diferente a los zombies normales ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
a partir de la 8 oleada empieza a haber lag, intenta buscar algún sistema para hacer que a partir de dicha oleada haya algun sistema para hacer que se disminuya el lag
User prompt
vamos a hacerle un rework al zombie sol: quitale el efecto del aura, elimina ese efecto, que simplemente esté el asset sin ningun efecto. quiero que haya un maximo de 1 zombie sol por oleada, es decir, si hay un 75% de que aparezca, pues si aparece que aparezca solo 1, no más. y tambien que cuando muera deje aparecer un sol, con el asset de los soles normales, y que al recogerlo le otorgue 25 soles al jugador. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que los soles que caen del cielo caigan cada 10 segundos en vez de cada 15. tambien añade un nuevo tipo de zombie, el zombie sol, este es un zombie normal pero tiene un aura amarilla, tiene una probabilidad de salir un 0% en la primera oleada 75% en la segunda oleada, 50% en la tercera 25% en la cuarta y a partir de la 5 no puede salir. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que se empiezen con 500 soles iniciales
User prompt
en el juego has hecho que cuando mueren los zombies de la primera oleada inicie la 2, que cuando mueran los de la segunda inicie la tercera, elimina esa opcion, a partir de ahora cada oleada dura 22 segundos, una vez pasen 22 segundos aparece la siguiente oleada sin importar si han muerto todos los zombies de la anterior oleada o no
User prompt
hay un error, se te ha olvidado poner que al empezar la partida, hasta que aparezca la primera oleada de zombies deben de pasar 10 segundos
User prompt
haz que se empiezen con 350 soles iniciales en vez de 300 y que los primeros zombies tarden 10 segundos en aparecer en vez de 5
User prompt
ahora quiero que añadas al zombie cubo, este será exactamente como un zombie normal, pero tendrá un asset diferente, y tendrá 375 puntos de vida, cuando sus puntos de vida estén por debajo de 191 su asset será el de un zombie normal, este aparece a partir de la oleada 3, y sale solo 1, luego a partir de ahi salen entre 2-3 luego 3-4, despues 4-5 y asi progresivamente
User prompt
desactiva que matar zombies te otorgue soles, eso no debe de ser asi. tambien añade en cada linea un cortacesped en la izquierda, y que si algun zombie toca el cortacesped este comenzará a desplazarse hasta la derecha del mapa, una vez llegue al borde del mapa desaparecerá, durante su recorrido, si toca a algun zombie, el zombie morirá instantaneamente sin importar cuanta vida tenga, tambien quiero que cuando un zombie DE CUALQUIER linea toca el borde, saldrá el game over ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
quiero que añadas que el zombie cono, si su salud está por debajo de 191 su asset pasará a ser el del zombie normal. tambien quiero que hagas que en la segunda oleada aparecen entre 3-4 zombies normales y entre 1-2 zombies de cono
User prompt
ahora quiero que añadas un nuevo tipo de zombie, se llamará zombie cono, sera como el zombie normal, pero tendrá 300 de vida, en el resto de cosas funciona igual, pero el zombie cono solo puede aparecer a partir de la segundo oleada, es decir, no puede aparecer en la primera
User prompt
perfecto! añadamos una nueva planta! la nuez, esta funciona de la siguiente manera, tiene 300 puntos de salud, tiene un asset determinado, pero si tiene menos de 150 puntos de salud pasará a tener otro asset diferente. cuesta 50 soles, una vez coloques una nuez, no puedes volver a colocar una nuez hasta que pasen 10 segundos, asi que si colocas una nuez, el icono de esta se pondrá gris y no podras colocar una nuez, y una vez pasen los 10 segundos volverá a su estado normal. esta planta no ataca. su icono esta junto al de las demas plantas abajo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
hay una funcion que al hacer click en una planta a cambio de 25 soles se hace mas grande, quiero que elimines esa funcion, si clickeas una planta no puedes hacerla mas grande a cambio de soles elimina esa funcion. tmabien quiero que elimines el "sanctuary" para perder debe de un zombie tocar el lateral izquierdo de la pantalla. quiero que añadas la pala, que funcione igual que los iconos pero que este situado arriba de la pantalla, y que al hacer click sobre él si despues haces click en una planta la borras
Remix started
Copy pvz a new awaken
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Lawnmower = Container.expand(function (row) { var self = Container.call(this); self.gridY = row; self.speed = 0; self.isActivated = false; self.x = gridStartX - 100; self.y = gridStartY + row * cellSize; var graphics = self.attachAsset('lawnmower', { anchorX: 0.5, anchorY: 0.5 }); self.activate = function () { if (!self.isActivated) { self.isActivated = true; self.speed = 6.4; } }; self.update = function () { if (self.isActivated) { self.x += self.speed; // Check collision with zombies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.gridY === self.gridY && self.intersects(enemy)) { enemy.die(); } } // Remove lawnmower when it reaches right edge if (self.x > 2148) { self.removeFromGame(); } } }; self.removeFromGame = function () { for (var i = 0; i < lawnmowers.length; i++) { if (lawnmowers[i] === self) { lawnmowers.splice(i, 1); break; } } self.destroy(); }; return self; }); var PlantBase = Container.expand(function (plantType) { var self = Container.call(this); self.plantType = plantType; self.health = 100; self.maxHealth = 100; self.cost = 50; self.shootTimer = 0; self.shootDelay = 72; self.gridX = 0; self.gridY = 0; self.level = 1; var graphics = self.attachAsset(plantType, { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFF0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { // Clean up projectile tracking for this plant var plantId = self.gridX + '_' + self.gridY; if (plantProjectileCount[plantId]) { delete plantProjectileCount[plantId]; } plants[self.gridY][self.gridX] = null; self.destroy(); }; return self; }); var Wallnut = PlantBase.expand(function () { var self = PlantBase.call(this, 'wallnut'); self.cost = 50; self.health = 300; self.maxHealth = 300; self.isDamaged = false; var originalTakeDamage = self.takeDamage; self.takeDamage = function (damage) { originalTakeDamage(damage); // Check if wallnut should switch to damaged appearance if (self.health < 150 && !self.isDamaged) { self.isDamaged = true; // Remove current graphics and add damaged version self.removeChild(self.children[0]); var damagedGraphics = self.attachAsset('wallnut_damaged', { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); var Sunflower = PlantBase.expand(function () { var self = PlantBase.call(this, 'sunflower'); self.cost = 50; self.sunTimer = 0; self.update = function () { self.sunTimer++; if (self.sunTimer >= 1080) { // 18 seconds var sun = new Sun(self.x, self.y - 60); suns.push(sun); game.addChild(sun); self.sunTimer = 0; LK.effects.flashObject(self, 0xFFFF00, 300); } }; return self; }); var SnowPea = PlantBase.expand(function () { var self = PlantBase.call(this, 'snowpea'); self.cost = 175; self.update = function () { self.shootTimer++; if (self.shootTimer >= self.shootDelay) { var target = self.findTarget(); if (target) { self.shoot(target); self.shootTimer = 0; } } }; self.findTarget = function () { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.gridY === self.gridY && enemy.x > self.x) { return enemy; } } return null; }; self.shoot = function (target) { var plantId = self.gridX + '_' + self.gridY; // Unique plant identifier var snowball = createProjectile('snowball', self.x, self.y, 6.4, 20 * self.level, plantId); if (snowball) { LK.getSound('shoot').play(); } }; return self; }); var Repetidora = PlantBase.expand(function () { var self = PlantBase.call(this, 'peashooter'); self.cost = 200; self.isShootingSecondPea = false; self.secondPeaTimer = 0; self.update = function () { self.shootTimer++; if (self.shootTimer >= self.shootDelay) { var target = self.findTarget(); if (target && !self.isShootingSecondPea) { self.shoot(target); self.shootTimer = 0; self.isShootingSecondPea = true; self.secondPeaTimer = 18; // 0.3 seconds at 60fps } } // Handle second pea timing if (self.isShootingSecondPea) { self.secondPeaTimer--; if (self.secondPeaTimer <= 0) { var target = self.findTarget(); if (target) { self.shoot(target); } self.isShootingSecondPea = false; } } }; self.findTarget = function () { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.gridY === self.gridY && enemy.x > self.x) { return enemy; } } return null; }; self.shoot = function (target) { var plantId = self.gridX + '_' + self.gridY; // Unique plant identifier var pea = createProjectile('pea', self.x, self.y, 6.4, 20 * self.level, plantId); if (pea) { LK.getSound('shoot').play(); } }; return self; }); var Peashooter = PlantBase.expand(function () { var self = PlantBase.call(this, 'peashooter'); self.cost = 100; self.update = function () { self.shootTimer++; if (self.shootTimer >= self.shootDelay) { var target = self.findTarget(); if (target) { self.shoot(target); self.shootTimer = 0; } } }; self.findTarget = function () { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.gridY === self.gridY && enemy.x > self.x) { return enemy; } } return null; }; self.shoot = function (target) { var plantId = self.gridX + '_' + self.gridY; // Unique plant identifier var pea = createProjectile('pea', self.x, self.y, 6.4, 20 * self.level, plantId); if (pea) { LK.getSound('shoot').play(); } }; return self; }); var Projectile = Container.expand(function (type, startX, startY, speed, damage) { var self = Container.call(this); self.speed = speed; self.damage = damage; self.x = startX; self.y = startY; self.type = type; self.gridY = Math.floor((startY - gridStartY + cellSize / 2) / cellSize); self.ownerId = null; // Plant that owns this projectile var graphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.x += self.speed; if (self.x > 2048) { self.removeFromGame(); return; } // Only check collisions with zombies in same row and within 100px range for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.gridY === self.gridY && Math.abs(enemy.x - self.x) <= 100 && self.intersects(enemy)) { enemy.takeDamage(self.damage); self.removeFromGame(); break; } } }; self.removeFromGame = function () { // Decrease projectile count for the plant that owns this projectile if (self.ownerId && plantProjectileCount[self.ownerId]) { plantProjectileCount[self.ownerId]--; } for (var i = 0; i < projectiles.length; i++) { if (projectiles[i] === self) { projectiles.splice(i, 1); break; } } // Return to pool instead of destroying self.returnToPool(); }; self.returnToPool = function () { if (projectilePool.length < maxPoolSize) { self.removeChildren(); if (self.parent) { self.parent.removeChild(self); } projectilePool.push(self); } else { self.destroy(); } }; return self; }); var SlowProjectile = Projectile.expand(function (type, startX, startY, speed, damage) { var self = Projectile.call(this, type, startX, startY, speed, damage); var originalUpdate = self.update; self.update = function () { originalUpdate(); // Only check collisions with zombies in same row and within 100px range for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.gridY === self.gridY && Math.abs(enemy.x - self.x) <= 100 && self.intersects(enemy)) { enemy.applySlowEffect(); break; } } }; return self; }); var Sun = Container.expand(function (x, y) { var self = Container.call(this); self.x = x; self.y = y; self.value = 25; self.collectTimer = 0; var graphics = self.attachAsset('sun', { anchorX: 0.5, anchorY: 0.5 }); self.down = function (x, y, obj) { sunPoints += self.value; updateSunDisplay(); self.collect(); }; self.collect = function () { for (var i = 0; i < suns.length; i++) { if (suns[i] === self) { suns.splice(i, 1); break; } } self.destroy(); }; self.update = function () { self.collectTimer++; if (self.collectTimer > 720) { // 12 seconds timeout self.collect(); } }; return self; }); var ZombieBase = Container.expand(function (zombieType) { var self = Container.call(this); self.zombieType = zombieType; self.health = 190; self.maxHealth = 190; self.speed = 0.8; self.damage = 25; self.gridY = 0; self.attackTimer = 0; self.slowTimer = 0; self.originalSpeed = self.speed; var graphics = self.attachAsset(zombieType, { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFF0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { self.die(); } }; self.die = function () { // Check if this is Zombiestein being killed if (self.zombieType === 'zombiestein') { zombiesteinAlive = false; } for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.applySlowEffect = function () { self.slowTimer = 180; self.speed = self.originalSpeed * 0.5; graphics.tint = 0x81C784; }; self.update = function () { if (self.slowTimer > 0) { self.slowTimer--; if (self.slowTimer === 0) { self.speed = self.originalSpeed; graphics.tint = 0xFFFFFF; } } var plantInFront = self.getPlantInFront(); if (plantInFront) { self.attackTimer++; if (self.attackTimer >= 72) { plantInFront.takeDamage(self.damage); self.attackTimer = 0; } } else { self.x -= self.speed; // Check if zombie reached left screen edge if (self.x < 0) { LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); return; } // Check collision with lawnmower for (var i = 0; i < lawnmowers.length; i++) { var lawnmower = lawnmowers[i]; if (lawnmower.gridY === self.gridY && self.x <= lawnmower.x + 60 && !lawnmower.isActivated) { lawnmower.activate(); } } } }; self.getPlantInFront = function () { var cellX = Math.floor((self.x - gridStartX) / cellSize); if (cellX >= 0 && cellX < gridCols && plants[self.gridY] && plants[self.gridY][cellX]) { return plants[self.gridY][cellX]; } return null; }; return self; }); var Zombiestein = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'zombiestein'); self.health = 3000; self.maxHealth = 3000; self.speed = 0.92; // 15% faster than original 0.8 speed self.originalSpeed = 0.92; self.damage = 500; // 500 damage per second self.isSummoning = false; self.hasSpawnedZombidito = false; self.originalUpdate = self.update; self.update = function () { // Check if health is below 1500 and hasn't spawned Zombidito yet if (self.health < 1500 && !self.hasSpawnedZombidito && !self.isSummoning) { self.isSummoning = true; self.speed = 0; // Stop moving completely // Wait 1.5 seconds then spawn Zombidito LK.setTimeout(function () { // Resume movement self.speed = self.originalSpeed; self.isSummoning = false; self.hasSpawnedZombidito = true; // Calculate position 3 cells ahead (in front of Zombiestein) var spawnX = self.x - 3 * cellSize; var spawnY = self.y; // Create Zombidito with explosion effect var zombidito = new Zombidito(); zombidito.x = spawnX; zombidito.y = spawnY; zombidito.gridY = self.gridY; enemies.push(zombidito); game.addChild(zombidito); // Create explosion effect like miner zombie var explosion = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: spawnX, y: spawnY, scaleX: 2, scaleY: 2 }); game.addChild(explosion); // Animate explosion and remove it tween(explosion, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); }, 1500); } // Call original update only if not summoning (stopped) if (!self.isSummoning) { self.originalUpdate(); } }; return self; }); var Zombie = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'zombie'); self.health = 190; self.maxHealth = 190; self.speed = 0.8; self.originalSpeed = 0.8; return self; }); var Zombidito = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'zombie'); self.health = 250; self.maxHealth = 250; self.speed = 0.8; self.originalSpeed = 0.8; self.damage = 50; // 50 damage per second // Scale down the graphics to make it smaller var graphics = self.children[0]; if (graphics) { graphics.scaleX = 0.7; graphics.scaleY = 0.7; } return self; }); var SunZombie = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'sun_zombie'); self.health = 190; self.maxHealth = 190; self.speed = 0.8; self.originalSpeed = 0.8; // Override die function to drop a sun when killed var originalDie = self.die; self.die = function () { // Create a sun at zombie's position var droppedSun = new Sun(self.x, self.y); suns.push(droppedSun); game.addChild(droppedSun); // Call original die function originalDie(); }; return self; }); var MinerZombie = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'miner_zombie'); self.health = 190; self.maxHealth = 190; self.speed = 0.8; self.originalSpeed = 0.8; // Override the initialization to spawn at 5th column and show explosion self.initializeMiner = function (row) { self.gridY = row; // Position at 5th column (index 4) self.x = gridStartX + 4 * cellSize; self.y = gridStartY + row * cellSize; // Create explosion effect var explosion = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, scaleX: 2, scaleY: 2 }); game.addChild(explosion); // Animate explosion and remove it tween(explosion, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); }; return self; }); var ConeZombie = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'cone_zombie'); self.health = 300; self.maxHealth = 300; self.speed = 0.8; self.originalSpeed = 0.8; self.hasChangedAsset = false; var originalTakeDamage = self.takeDamage; self.takeDamage = function (damage) { originalTakeDamage(damage); // Check if cone zombie should switch to normal zombie appearance if (self.health < 191 && !self.hasChangedAsset) { self.hasChangedAsset = true; // Remove current graphics and add normal zombie version self.removeChild(self.children[0]); var normalGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); var BucketZombie = ZombieBase.expand(function () { var self = ZombieBase.call(this, 'bucket_zombie'); self.health = 375; self.maxHealth = 375; self.speed = 0.8; self.originalSpeed = 0.8; self.hasChangedAsset = false; var originalTakeDamage = self.takeDamage; self.takeDamage = function (damage) { originalTakeDamage(damage); // Check if bucket zombie should switch to normal zombie appearance if (self.health < 191 && !self.hasChangedAsset) { self.hasChangedAsset = true; // Remove current graphics and add normal zombie version self.removeChild(self.children[0]); var normalGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1B5E20 }); /**** * Game Code ****/ // Sounds // UI and grid // Undead enemies // Projectiles // Plant defenders // Grid setup - bigger grid centered on screen var gridRows = 5; var gridCols = 9; var cellSize = 144; var gridStartX = (2048 - gridCols * cellSize) / 2; var gridStartY = (2732 - gridRows * cellSize) / 2; // Game state var plants = []; var enemies = []; var projectiles = []; var suns = []; var lawnmowers = []; var sunPoints = 500; var lives = 3; var currentWave = 1; var wavesCompleted = 0; var totalWaves = 10; var waveInProgress = false; var enemiesInWave = 0; var enemiesKilled = 0; var sunTimer = 0; var waveStartTick = 0; var nextWaveScheduled = false; var sunZombieSpawnedThisWave = false; // Miner zombie tracking var minerZombiesSpawnedThisWave = 0; var maxMinerZombiesPerWave = 1; // Zombiestein tracking var zombiesteinSpawned = false; var zombiesteinAlive = false; // Performance optimization variables var maxObjectsPerType = 50; var cleanupCounter = 0; var lagOptimizationActive = false; // Projectile pool for object reuse var projectilePool = []; var maxPoolSize = 20; // Track active projectiles per plant var plantProjectileCount = {}; // Selected plant type var selectedPlantType = null; var plantTypes = ['sunflower', 'peashooter', 'wallnut', 'repetidora']; var plantCosts = { 'peashooter': 100, 'sunflower': 50, 'wallnut': 50, 'repetidora': 200 }; // Wallnut cooldown system var wallnutCooldown = 0; var wallnutCooldownTime = 720; // 12 seconds at 60fps // Ghost plant for preview var ghostPlant = null; // Initialize grid cells array var gridCells = []; // Initialize grid for (var row = 0; row < gridRows; row++) { plants[row] = []; gridCells[row] = []; for (var col = 0; col < gridCols; col++) { plants[row][col] = null; var cell = LK.getAsset('gridcell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, x: gridStartX + col * cellSize, y: gridStartY + row * cellSize }); gridCells[row][col] = cell; game.addChild(cell); } } // Initialize lawnmowers for (var row = 0; row < gridRows; row++) { var lawnmower = new Lawnmower(row); lawnmowers.push(lawnmower); game.addChild(lawnmower); } // Shovel tool for removing plants var selectedTool = null; // 'shovel' or null var shovelButton = LK.getAsset('shovel', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 100, scaleX: 1.5, scaleY: 1.5 }); shovelButton.interactive = true; shovelButton.buttonMode = true; shovelButton.down = function (x, y, obj) { console.log("Shovel button clicked"); if (selectedTool === 'shovel') { selectedTool = null; shovelButton.alpha = 0.8; shovelButton.scaleX = shovelButton.scaleY = 1.5; } else { selectedTool = 'shovel'; selectedPlantType = null; shovelButton.alpha = 1.0; shovelButton.scaleX = shovelButton.scaleY = 1.8; updatePlantSelection(); } }; shovelButton.alpha = 0.8; game.addChild(shovelButton); // UI Elements var sunDisplay = new Text2('Sun: ' + sunPoints, { size: 60, fill: 0xFFEB3B }); sunDisplay.anchor.set(0, 0); sunDisplay.x = 120; // Offset from left edge to avoid menu icon LK.gui.topLeft.addChild(sunDisplay); var livesDisplay = new Text2('Lives: ' + lives, { size: 60, fill: 0xF44336 }); livesDisplay.anchor.set(0, 0); livesDisplay.y = 80; LK.gui.topRight.addChild(livesDisplay); var waveDisplay = new Text2('Wave: ' + currentWave + '/' + totalWaves, { size: 50, fill: 0x4CAF50 }); waveDisplay.anchor.set(0.5, 0); LK.gui.top.addChild(waveDisplay); // Plant selection UI at bottom var plantButtons = []; var uiY = 2732 - 150; for (var i = 0; i < plantTypes.length; i++) { var button = LK.getAsset(plantTypes[i], { anchorX: 0.5, anchorY: 0.5, x: 200 + i * 220, y: uiY, scaleX: 2.25, scaleY: 2.25 }); button.plantType = plantTypes[i]; button.interactive = true; // Enable button interactivity button.buttonMode = true; // Make it behave like a button button.down = function (x, y, obj) { console.log("Plant button clicked:", this.plantType, "Cost:", plantCosts[this.plantType], "Current suns:", sunPoints); // Check wallnut cooldown if (this.plantType === 'wallnut' && wallnutCooldown > 0) { return; // Don't allow selection during cooldown } if (sunPoints >= plantCosts[this.plantType]) { selectedPlantType = this.plantType; selectedTool = null; shovelButton.alpha = 0.8; shovelButton.scaleX = shovelButton.scaleY = 1.5; updatePlantSelection(); } }; game.addChild(button); plantButtons.push(button); // Add cost text var costText = new Text2(plantCosts[plantTypes[i]], { size: 40, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0); costText.x = button.x; costText.y = button.y + 80; game.addChild(costText); } function updatePlantSelection() { for (var i = 0; i < plantButtons.length; i++) { if (plantButtons[i].plantType === selectedPlantType) { plantButtons[i].alpha = 1.0; plantButtons[i].scaleX = plantButtons[i].scaleY = 2.55; } else if (plantButtons[i].plantType === 'wallnut' && wallnutCooldown > 0) { // Gray out wallnut during cooldown plantButtons[i].alpha = 0.3; plantButtons[i].scaleX = plantButtons[i].scaleY = 2.25; plantButtons[i].tint = 0x888888; } else { plantButtons[i].alpha = 0.6; plantButtons[i].scaleX = plantButtons[i].scaleY = 2.25; plantButtons[i].tint = 0xFFFFFF; } } // Remove existing ghost plant if (ghostPlant) { ghostPlant.destroy(); ghostPlant = null; } // Create new ghost plant if a type is selected if (selectedPlantType) { ghostPlant = LK.getAsset(selectedPlantType, { anchorX: 0.5, anchorY: 0.5, alpha: 0.6, tint: 0x88FF88 }); game.addChild(ghostPlant); } // Highlight valid placement squares for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { if (selectedPlantType && canPlacePlant(col, row, selectedPlantType)) { // Green highlight for valid empty squares gridCells[row][col].tint = 0x00FF00; gridCells[row][col].alpha = 0.7; } else if (selectedPlantType && plants[row][col] !== null) { // Red highlight for occupied squares when plant is selected gridCells[row][col].tint = 0xFF0000; gridCells[row][col].alpha = 0.5; } else if (selectedTool === 'shovel' && plants[row][col] !== null) { // Yellow highlight for plants that can be removed with shovel gridCells[row][col].tint = 0xFFFF00; gridCells[row][col].alpha = 0.7; } else { // Normal appearance for unselected or invalid squares gridCells[row][col].tint = 0xFFFFFF; gridCells[row][col].alpha = 0.3; } } } } function updateSunDisplay() { sunDisplay.setText('Sun: ' + sunPoints); } function updateLivesDisplay() { livesDisplay.setText('Lives: ' + lives); } function updateWaveDisplay() { waveDisplay.setText('Wave: ' + currentWave + '/' + totalWaves); } function createProjectile(type, startX, startY, speed, damage, plantId) { // Check if plant already has 2 active projectiles if (!plantProjectileCount[plantId]) { plantProjectileCount[plantId] = 0; } if (plantProjectileCount[plantId] >= 2) { return null; // Don't create projectile if plant already has 2 } var projectile; if (projectilePool.length > 0) { // Reuse from pool projectile = projectilePool.pop(); projectile.speed = speed; projectile.damage = damage; projectile.x = startX; projectile.y = startY; projectile.type = type; projectile.gridY = Math.floor((startY - gridStartY + cellSize / 2) / cellSize); projectile.ownerId = plantId; // Re-add graphics var graphics = projectile.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); } else { // Create new projectile projectile = new Projectile(type, startX, startY, speed, damage); projectile.ownerId = plantId; } plantProjectileCount[plantId]++; projectiles.push(projectile); game.addChild(projectile); return projectile; } function performanceCleanup() { // Clean up destroyed objects that might still be in arrays for (var i = enemies.length - 1; i >= 0; i--) { if (!enemies[i].parent) { enemies.splice(i, 1); } } for (var i = projectiles.length - 1; i >= 0; i--) { if (!projectiles[i].parent) { projectiles.splice(i, 1); } } for (var i = suns.length - 1; i >= 0; i--) { if (!suns[i].parent) { suns.splice(i, 1); } } // Clean up projectile tracking for destroyed plants for (var plantId in plantProjectileCount) { var coords = plantId.split('_'); var x = parseInt(coords[0]); var y = parseInt(coords[1]); if (y >= 0 && y < gridRows && x >= 0 && x < gridCols && !plants[y][x]) { delete plantProjectileCount[plantId]; } } // Limit maximum objects if we have too many if (enemies.length > maxObjectsPerType) { for (var i = 0; i < enemies.length - maxObjectsPerType; i++) { if (enemies[i]) { enemies[i].destroy(); } } } if (projectiles.length > maxObjectsPerType) { for (var i = 0; i < projectiles.length - maxObjectsPerType; i++) { if (projectiles[i]) { projectiles[i].removeFromGame(); } } } if (suns.length > 20) { for (var i = 0; i < suns.length - 20; i++) { if (suns[i]) { suns[i].destroy(); } } } // Limit projectile pool size if (projectilePool.length > maxPoolSize) { for (var i = maxPoolSize; i < projectilePool.length; i++) { if (projectilePool[i]) { projectilePool[i].destroy(); } } projectilePool.length = maxPoolSize; } } function getGridPosition(x, y) { var gridX = Math.floor((x - gridStartX + cellSize / 2) / cellSize); var gridY = Math.floor((y - gridStartY + cellSize / 2) / cellSize); if (gridX >= 0 && gridX < gridCols && gridY >= 0 && gridY < gridRows) { return { x: gridX, y: gridY }; } return null; } function canPlacePlant(gridX, gridY, plantType) { // Check if grid position is valid if (gridX < 0 || gridX >= gridCols || gridY < 0 || gridY >= gridRows) { return false; } // Check wallnut cooldown if (plantType === 'wallnut' && wallnutCooldown > 0) { return false; } // Check if square is empty and player has enough suns return plants[gridY][gridX] === null && sunPoints >= plantCosts[plantType]; } function placePlant(gridX, gridY, plantType) { // Double-check that placement is valid before proceeding if (!canPlacePlant(gridX, gridY, plantType)) { return false; } var plant; switch (plantType) { case 'peashooter': plant = new Peashooter(); break; case 'sunflower': plant = new Sunflower(); break; case 'wallnut': plant = new Wallnut(); break; case 'snowpea': plant = new SnowPea(); break; case 'repetidora': plant = new Repetidora(); break; default: return false; } // Set plant position and grid reference plant.gridX = gridX; plant.gridY = gridY; plant.x = gridStartX + gridX * cellSize; plant.y = gridStartY + gridY * cellSize; // Place plant in grid and add to game plants[gridY][gridX] = plant; game.addChild(plant); // Deduct cost and update UI sunPoints -= plantCosts[plantType]; updateSunDisplay(); LK.getSound('plant').play(); // Start wallnut cooldown if placing wallnut if (plantType === 'wallnut') { wallnutCooldown = wallnutCooldownTime; selectedPlantType = null; // Deselect wallnut after placing updatePlantSelection(); } // Flash the grid cell to show successful placement LK.effects.flashObject(gridCells[gridY][gridX], 0x00FF00, 300); return true; } function spawnZombie(type, row) { var zombie; switch (type) { case 'zombie': zombie = new Zombie(); break; case 'cone_zombie': zombie = new ConeZombie(); break; case 'bucket_zombie': zombie = new BucketZombie(); break; case 'sun_zombie': zombie = new SunZombie(); break; case 'miner_zombie': zombie = new MinerZombie(); // Special positioning for miner zombie.initializeMiner(row); enemies.push(zombie); game.addChild(zombie); return; // Return early since positioning is handled case 'zombiestein': zombie = new Zombiestein(); break; default: return; } zombie.gridY = row; zombie.x = 2100; zombie.y = gridStartY + row * cellSize; enemies.push(zombie); game.addChild(zombie); } function startWave() { if (waveInProgress) { return; } waveInProgress = true; waveStartTick = LK.ticks; // Record when wave started sunZombieSpawnedThisWave = false; // Reset sun zombie spawn flag for this wave // Reset and update miner zombie tracking for this wave minerZombiesSpawnedThisWave = 0; if (currentWave >= 8) { maxMinerZombiesPerWave = 2; } else { maxMinerZombiesPerWave = 1; } var bucketZombiesLeft = 0; if (currentWave === 1) { // Wave 1: 3-4 normal zombies var normalCount = Math.floor(Math.random() * 2) + 3; for (var i = 0; i < normalCount; i++) { var row = Math.floor(Math.random() * gridRows); spawnZombie('zombie', row); } enemiesInWave = normalCount; } else if (currentWave === 2) { // Wave 2: 3-4 normal zombies, 75% chance for 1 sun zombie, 1 cone zombie var normalCount = Math.floor(Math.random() * 2) + 3; var sunZombieCount = Math.random() < 0.75 ? 1 : 0; var coneCount = 1; // Spawn normal zombies for (var i = 0; i < normalCount; i++) { var row = Math.floor(Math.random() * gridRows); spawnZombie('zombie', row); } // Spawn sun zombie if chance succeeds if (sunZombieCount > 0) { var row = Math.floor(Math.random() * gridRows); spawnZombie('sun_zombie', row); sunZombieSpawnedThisWave = true; } // Spawn cone zombie var row = Math.floor(Math.random() * gridRows); spawnZombie('cone_zombie', row); enemiesInWave = normalCount + sunZombieCount + coneCount; } else if (currentWave === 3) { bucketZombiesLeft = 1; // Only 1 bucket zombie in wave 3 enemiesInWave = Math.floor(Math.random() * 2) + 3 + Math.floor(Math.random() * 2) + 1 + bucketZombiesLeft; // normal + cone + bucket } else if (currentWave === 4) { bucketZombiesLeft = Math.floor(Math.random() * 2) + 2; // 2-3 bucket zombies enemiesInWave = Math.floor(Math.random() * 2) + 3 + Math.floor(Math.random() * 2) + 1 + bucketZombiesLeft; } else if (currentWave === 5) { bucketZombiesLeft = Math.floor(Math.random() * 2) + 3; // 3-4 bucket zombies enemiesInWave = Math.floor(Math.random() * 2) + 3 + Math.floor(Math.random() * 2) + 1 + bucketZombiesLeft; } else { // From wave 6+: spawn 50% fewer zombies but increase cone/bucket probability by 25% bucketZombiesLeft = Math.floor((Math.floor(Math.random() * 2) + 4 + (currentWave - 6)) * 0.5); // 50% fewer bucket zombies enemiesInWave = Math.floor((5 + currentWave * 2) * 0.5); // 50% fewer total enemies // Spawn Zombiestein in wave 10 (only once per game) if (currentWave === 10 && !zombiesteinSpawned) { var zombiesteinRow = Math.floor(Math.random() * gridRows); spawnZombie('zombiestein', zombiesteinRow); zombiesteinSpawned = true; zombiesteinAlive = true; enemiesInWave++; // Add Zombiestein to enemy count } } enemiesKilled = 0; // Only set up spawn timer for waves 3 and above (waves 1-2 spawn immediately) if (currentWave >= 3) { var normalZombiesLeft = Math.floor(Math.random() * 2) + 3; var coneZombiesLeft = Math.floor(Math.random() * 2) + 1; var spawnTimer = LK.setInterval(function () { if (enemiesInWave > 0) { var row = Math.floor(Math.random() * gridRows); var zombieType; // Determine sun zombie probability based on wave var sunZombieProbability = 0; if (currentWave === 3) sunZombieProbability = 0.50;else if (currentWave === 4) sunZombieProbability = 0.25; // Check if we should spawn a sun zombie (only waves 3-4, max 1 per wave) var shouldSpawnSunZombie = currentWave >= 3 && currentWave <= 4 && !sunZombieSpawnedThisWave && Math.random() < sunZombieProbability; // Check if we should spawn a miner zombie (waves 6+, 100% chance, max per wave limit) var shouldSpawnMinerZombie = currentWave >= 6 && minerZombiesSpawnedThisWave < maxMinerZombiesPerWave && Math.random() < 1.0; if (shouldSpawnSunZombie) { zombieType = 'sun_zombie'; sunZombieSpawnedThisWave = true; // Mark that sun zombie has been spawned this wave } else if (shouldSpawnMinerZombie) { zombieType = 'miner_zombie'; minerZombiesSpawnedThisWave++; // Increment miner count for this wave } else if (currentWave >= 3) { // From wave 3 onwards, include bucket zombies // For wave 6+, increase cone and bucket zombie probability by 25% if (normalZombiesLeft > 0 && coneZombiesLeft > 0 && bucketZombiesLeft > 0) { var rand = Math.random(); if (currentWave >= 6) { // Wave 6+: 25% normal, 37.5% cone, 37.5% bucket (25% more cone/bucket) if (rand < 0.25) { zombieType = 'zombie'; normalZombiesLeft--; } else if (rand < 0.625) { zombieType = 'cone_zombie'; coneZombiesLeft--; } else { zombieType = 'bucket_zombie'; bucketZombiesLeft--; } } else { // Wave 3-5: original probabilities if (rand < 0.4) { zombieType = 'zombie'; normalZombiesLeft--; } else if (rand < 0.7) { zombieType = 'cone_zombie'; coneZombiesLeft--; } else { zombieType = 'bucket_zombie'; bucketZombiesLeft--; } } } else if (normalZombiesLeft > 0 && coneZombiesLeft > 0) { if (currentWave >= 6) { // Wave 6+: 35% normal, 65% cone (25% more cone) zombieType = Math.random() < 0.35 ? 'zombie' : 'cone_zombie'; } else { // Wave 3-5: original 60/40 split zombieType = Math.random() < 0.6 ? 'zombie' : 'cone_zombie'; } if (zombieType === 'zombie') { normalZombiesLeft--; } else { coneZombiesLeft--; } } else if (normalZombiesLeft > 0 && bucketZombiesLeft > 0) { if (currentWave >= 6) { // Wave 6+: 35% normal, 65% bucket (25% more bucket) zombieType = Math.random() < 0.35 ? 'zombie' : 'bucket_zombie'; } else { // Wave 3-5: original 60/40 split zombieType = Math.random() < 0.6 ? 'zombie' : 'bucket_zombie'; } if (zombieType === 'zombie') { normalZombiesLeft--; } else { bucketZombiesLeft--; } } else if (coneZombiesLeft > 0 && bucketZombiesLeft > 0) { zombieType = Math.random() < 0.5 ? 'cone_zombie' : 'bucket_zombie'; if (zombieType === 'cone_zombie') { coneZombiesLeft--; } else { bucketZombiesLeft--; } } else if (normalZombiesLeft > 0) { zombieType = 'zombie'; normalZombiesLeft--; } else if (coneZombiesLeft > 0) { zombieType = 'cone_zombie'; coneZombiesLeft--; } else if (bucketZombiesLeft > 0) { zombieType = 'bucket_zombie'; bucketZombiesLeft--; } else { zombieType = 'zombie'; // fallback } } else { // Fallback for other waves zombieType = Math.random() < 0.5 ? 'zombie' : 'cone_zombie'; } spawnZombie(zombieType, row); enemiesInWave--; } else { LK.clearInterval(spawnTimer); } }, 1200); } // Close the if (currentWave >= 3) block } // Mouse move handler to update ghost plant position game.move = function (x, y, obj) { if (ghostPlant && selectedPlantType) { var gridPos = getGridPosition(x, y); if (gridPos) { // Snap to grid position ghostPlant.x = gridStartX + gridPos.x * cellSize; ghostPlant.y = gridStartY + gridPos.y * cellSize; // Change color based on validity if (canPlacePlant(gridPos.x, gridPos.y, selectedPlantType)) { ghostPlant.tint = 0x88FF88; // Green tint for valid placement } else { ghostPlant.tint = 0xFF8888; // Red tint for invalid placement } } else { // Follow cursor if outside grid ghostPlant.x = x; ghostPlant.y = y; ghostPlant.tint = 0xFF8888; // Red tint when outside grid } } }; // Game input handling game.down = function (x, y, obj) { // Check if clicking on a sun for (var i = 0; i < suns.length; i++) { if (suns[i].intersects({ x: x, y: y, width: 1, height: 1 })) { return; // Let sun handle its own click } } // Check if using shovel to remove plant if (selectedTool === 'shovel') { var gridPos = getGridPosition(x, y); if (gridPos && plants[gridPos.y][gridPos.x] !== null) { // Remove the plant var plant = plants[gridPos.y][gridPos.x]; plant.destroy(); plants[gridPos.y][gridPos.x] = null; LK.effects.flashObject(gridCells[gridPos.y][gridPos.x], 0xFFFF00, 300); updatePlantSelection(); return; } else if (gridPos) { // Flash red if trying to remove from empty square LK.effects.flashObject(gridCells[gridPos.y][gridPos.x], 0xFF0000, 300); } else { // Clicking outside grid cancels shovel selection selectedTool = null; shovelButton.alpha = 0.8; shovelButton.scaleX = shovelButton.scaleY = 1.5; updatePlantSelection(); } } // Check if placing a plant else if (selectedPlantType) { var gridPos = getGridPosition(x, y); if (gridPos) { if (canPlacePlant(gridPos.x, gridPos.y, selectedPlantType)) { // Successfully place the plant if (placePlant(gridPos.x, gridPos.y, selectedPlantType)) { selectedPlantType = null; updatePlantSelection(); return; } } else if (plants[gridPos.y][gridPos.x] !== null) { // Flash red if trying to place on occupied square LK.effects.flashObject(gridCells[gridPos.y][gridPos.x], 0xFF0000, 300); } else if (sunPoints < plantCosts[selectedPlantType]) { // Flash yellow if not enough suns LK.effects.flashObject(gridCells[gridPos.y][gridPos.x], 0xFFFF00, 300); } } else { // Clicking outside grid cancels selection selectedPlantType = null; updatePlantSelection(); } } }; // Initialize UI updatePlantSelection(); updateSunDisplay(); updateLivesDisplay(); updateWaveDisplay(); // Start first wave after a delay LK.setTimeout(function () { startWave(); }, 10000); // Main game loop game.update = function () { // Enable lag optimization from wave 8 onwards if (currentWave >= 8 && !lagOptimizationActive) { lagOptimizationActive = true; console.log("Lag optimization activated for wave", currentWave); } // Performance cleanup every 3 seconds when lag optimization is active if (lagOptimizationActive) { cleanupCounter++; if (cleanupCounter >= 180) { // Every 3 seconds at 60fps performanceCleanup(); cleanupCounter = 0; } } // Update wallnut cooldown if (wallnutCooldown > 0) { wallnutCooldown--; if (wallnutCooldown === 0) { updatePlantSelection(); // Refresh button appearance when cooldown ends } } // Falling sun system - every 10 seconds (optimized for high waves) sunTimer++; var sunInterval = lagOptimizationActive ? 1080 : 720; // 18 seconds when optimized, 12 seconds normally if (sunTimer >= sunInterval) { // Only spawn sun if we don't have too many already if (suns.length < (lagOptimizationActive ? 8 : 15)) { var fallingSun = new Sun(Math.random() * (gridStartX + gridCols * cellSize - gridStartX) + gridStartX, -50); suns.push(fallingSun); game.addChild(fallingSun); // Animate falling sun tween(fallingSun, { y: Math.random() * 200 + 300 }, { duration: 2400, easing: tween.easeOut }); } sunTimer = 0; } // Check if current wave should end (22 seconds timer-based) if (waveInProgress && LK.ticks - waveStartTick >= 1584) { // 26.4 seconds at 60fps waveInProgress = false; wavesCompleted++; if (wavesCompleted >= totalWaves) { // Check if Zombiestein was spawned and is still alive if (zombiesteinSpawned && zombiesteinAlive) { // Don't end game until Zombiestein is defeated console.log("All waves completed but Zombiestein still alive!"); return; } LK.effects.flashScreen(0x4CAF50, 1000); LK.showYouWin(); return; } currentWave++; updateWaveDisplay(); // Start next wave after delay LK.setTimeout(function () { startWave(); }, 3000); } // Auto-start next wave if no wave in progress and we haven't completed all waves if (!waveInProgress && wavesCompleted < totalWaves && !nextWaveScheduled) { nextWaveScheduled = true; LK.setTimeout(function () { nextWaveScheduled = false; startWave(); }, 1000); } };
===================================================================
--- original.js
+++ change.js
@@ -152,8 +152,54 @@
}
};
return self;
});
+var Repetidora = PlantBase.expand(function () {
+ var self = PlantBase.call(this, 'peashooter');
+ self.cost = 200;
+ self.isShootingSecondPea = false;
+ self.secondPeaTimer = 0;
+ self.update = function () {
+ self.shootTimer++;
+ if (self.shootTimer >= self.shootDelay) {
+ var target = self.findTarget();
+ if (target && !self.isShootingSecondPea) {
+ self.shoot(target);
+ self.shootTimer = 0;
+ self.isShootingSecondPea = true;
+ self.secondPeaTimer = 18; // 0.3 seconds at 60fps
+ }
+ }
+ // Handle second pea timing
+ if (self.isShootingSecondPea) {
+ self.secondPeaTimer--;
+ if (self.secondPeaTimer <= 0) {
+ var target = self.findTarget();
+ if (target) {
+ self.shoot(target);
+ }
+ self.isShootingSecondPea = false;
+ }
+ }
+ };
+ self.findTarget = function () {
+ for (var i = 0; i < enemies.length; i++) {
+ var enemy = enemies[i];
+ if (enemy.gridY === self.gridY && enemy.x > self.x) {
+ return enemy;
+ }
+ }
+ return null;
+ };
+ self.shoot = function (target) {
+ var plantId = self.gridX + '_' + self.gridY; // Unique plant identifier
+ var pea = createProjectile('pea', self.x, self.y, 6.4, 20 * self.level, plantId);
+ if (pea) {
+ LK.getSound('shoot').play();
+ }
+ };
+ return self;
+});
var Peashooter = PlantBase.expand(function () {
var self = PlantBase.call(this, 'peashooter');
self.cost = 100;
self.update = function () {
@@ -613,13 +659,14 @@
// Track active projectiles per plant
var plantProjectileCount = {};
// Selected plant type
var selectedPlantType = null;
-var plantTypes = ['sunflower', 'peashooter', 'wallnut'];
+var plantTypes = ['sunflower', 'peashooter', 'wallnut', 'repetidora'];
var plantCosts = {
'peashooter': 100,
'sunflower': 50,
- 'wallnut': 50
+ 'wallnut': 50,
+ 'repetidora': 200
};
// Wallnut cooldown system
var wallnutCooldown = 0;
var wallnutCooldownTime = 720; // 12 seconds at 60fps
@@ -936,8 +983,11 @@
break;
case 'snowpea':
plant = new SnowPea();
break;
+ case 'repetidora':
+ plant = new Repetidora();
+ break;
default:
return false;
}
// Set plant position and grid reference