User prompt
Ok make it 30% after 25 wave
User prompt
Ok then: After the extra special weathers activate every wave ther change will increase by %5 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Do the same weather text thing for extra special weathers but extra special weathers will be golden and text like: Extra Bloody Rain ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the special weather change become %100. Add Extra special weather. Every weather has %30 change to be a extra special weather. Extra special weathers are same as special weathers but double effect. Like Bloody Rain = 4 times hp for all zombies ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Remove the descrpition when the text goes to bottom middle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the weather text at middle appear, also write what does that weather do too ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the weather text go to bottom middle instead of bottom left corner ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Lets add this: After the 10. wave, every wave there is a %5 change to have a special weather, like : Windy = All zombies are %50 faster Blood Smell = Double amouth of zombies Blood Rain = Every zombie has double hp Unlucky Clouds = There is %20 change to your gun dont shoot BLOOD MOON = ALL OF THEM The weather will written at the middle of the screen at the start for 3 second then it will shrink and move to left bottom corner. Weathers last for 1 wave. Blood Moon is very rare the other ones is all have equal change to happen. The special weather change increas by %2.5 every wave until its %100 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Lets try monster behavior Improments
User prompt
They are still coming too close they can throw from more far away
User prompt
Make them die with 1 hit, they are coming too close to throw a rock they can throw from more far, make then shake a bit when throwing the rock so they look like realy throwing ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Lets add a zombie that when it comes near enough its start throwing rock at me its has %10 change to replace a normal zombie after wave 25 rock will deal 5 damage and it will keep throwing rock until it dies. Make a texture for it and rock. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
more pls
User prompt
İts working now but it can be less close to center
User prompt
it still cant damage me. can we make it deal damage to me when he is close to center of screen ?
User prompt
They still cant deal damage to me
User prompt
they still cant deal damage to me
User prompt
Crayz zombies cant deal damage to me
User prompt
make a asset for splitter zombie and make its minis green
User prompt
Splitter zombie is cool! Lets make it appear after wave 18. Its has %15 change to replace a tough zombie
User prompt
make it 25
User prompt
TWİCE THE SPEED
User prompt
faster
User prompt
a bit more faster pls ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
İts working! Just make them a bit faster
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BloodSplat = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('blood', { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 300; // 5 seconds at 60fps self.fadeStartTime = 0; self.isFading = false; self.update = function () { self.lifetime--; // Start fading after 10 seconds if (self.lifetime <= 0 && !self.isFading) { self.isFading = true; tween(graphics, { alpha: 0 }, { duration: 3000, onFinish: function onFinish() { self.destroy(); } }); } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 20; self.dx = 0; self.dy = 0; self.active = true; self.bounced = false; self.bounceCount = 0; // Track number of bounces performed self.update = function () { if (!self.active) return; self.x += self.dx; self.y += self.dy; if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.active = false; } }; self.setDirection = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.dx = dx / distance * self.speed; self.dy = dy / distance * self.speed; // Rotate bullet to face direction of travel graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } }; self.bounce = function (hitMonster) { // Increment bounce count first self.bounceCount++; // Check if we've reached the bounce limit if (self.bounceCount >= bouncyBulletCount) { self.active = false; return; } // Find nearest monster regardless of distance var nearestMonster = null; var nearestDistance = Infinity; // No distance limit for (var i = 0; i < monsters.length; i++) { var monster = monsters[i]; if (!monster.active || monster === hitMonster) continue; var dx = monster.x - self.x; var dy = monster.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < nearestDistance) { nearestDistance = distance; nearestMonster = monster; } } if (nearestMonster) { self.bounced = true; self.bounceTarget = nearestMonster; // Store the target self.setDirection(nearestMonster.x, nearestMonster.y); } else { // No target found, deactivate bullet self.active = false; } }; return self; }); var Card = Container.expand(function (powerType) { var self = Container.call(this); // Card background var cardBg = LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.5 }); cardBg.tint = 0x2c3e50; self.addChild(cardBg); // Card title text var titleText = new Text2(getPowerTitle(powerType), { size: 50, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.y = -40; self.addChild(titleText); // Card description text var descText = new Text2(getPowerDescription(powerType), { size: 30, fill: 0xECF0F1 }); descText.anchor.set(0.5, 0.5); descText.y = 20; self.addChild(descText); self.powerType = powerType; // Card selection handler self.down = function (x, y, obj) { selectCard(self.powerType); }; return self; }); var CrayzZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('crayzZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; // Faster speed for more challenge self.health = 1; self.damage = 12; self.active = true; self.lastPlayerDistance = 0; self.spiralAngle = 0; // Angle for spiral movement self.crazyTimer = 0; // Timer for random direction changes self.suddenChangeX = 0; // Sudden movement offset X self.suddenChangeY = 0; // Sudden movement offset Y self.isChangingDirection = false; // Flag for direction change animation // Start with a random crazy movement pattern immediately self.startCrazyMovement = function () { if (self.isChangingDirection) return; self.isChangingDirection = true; // Random sudden movement direction var randomAngle = Math.random() * Math.PI * 2; var randomDistance = 100 + Math.random() * 200; var targetX = Math.cos(randomAngle) * randomDistance; var targetY = Math.sin(randomAngle) * randomDistance; // Animate sudden direction change tween(self, { suddenChangeX: targetX, suddenChangeY: targetY }, { duration: 300 + Math.random() * 400, easing: tween.easeInOut, onFinish: function onFinish() { // Reset and prepare for next crazy movement self.suddenChangeX = 0; self.suddenChangeY = 0; self.isChangingDirection = false; } }); }; self.update = function () { if (!self.active) return; // Screen center coordinates var screenCenterX = 1024; // Half of 2048 var screenCenterY = 1366; // Half of 2732 // Calculate distance from screen center var dxToCenter = screenCenterX - self.x; var dyToCenter = screenCenterY - self.y; var distanceToCenter = Math.sqrt(dxToCenter * dxToCenter + dyToCenter * dyToCenter); // Calculate distance to player for damage detection var dxToPlayer = player.x - self.x; var dyToPlayer = player.y - self.y; var distanceToPlayer = Math.sqrt(dxToPlayer * dxToPlayer + dyToPlayer * dyToPlayer); // Spiral movement around screen center self.spiralAngle += 0.1; // Spiral rotation speed // Calculate current position relative to screen center var currentAngle = Math.atan2(self.y - screenCenterY, self.x - screenCenterX); var currentRadius = distanceToCenter; // Gradually decrease radius to spiral inward var targetRadius = Math.max(50, currentRadius - 1); // Minimum radius of 50 pixels // Calculate new position on the spiral var newAngle = currentAngle + 0.1; // Spiral by rotating around center var newX = screenCenterX + Math.cos(newAngle) * targetRadius; var newY = screenCenterY + Math.sin(newAngle) * targetRadius; // Move toward the new spiral position var dx = newX - self.x; var dy = newY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate zombie to face movement direction graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } var currentDistance = distanceToPlayer; if (self.lastPlayerDistance > 50 && currentDistance <= 50) { player.takeDamage(self.damage); self.active = false; } self.lastPlayerDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.active = false; // Create blood explosion with spreading effect for (var i = 0; i < 15; i++) { var bloodSplat = new BloodSplat(); // Start at monster position bloodSplat.x = self.x; bloodSplat.y = self.y; bloodSplat.graphics = bloodSplat.children[0]; var scale = 0.3 + Math.random() * 1.2; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; // Calculate random spread destination var angle = Math.random() * Math.PI * 2; var distance = 150 + Math.random() * 300; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; // Keep within game bounds targetX = Math.max(50, Math.min(1998, targetX)); targetY = Math.max(50, Math.min(2682, targetY)); // Animate spreading with random delay and duration var delay = Math.random() * 200; var duration = 300 + Math.random() * 400; tween(bloodSplat, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut }); bloodSplats.push(bloodSplat); game.addChild(bloodSplat); } LK.setScore(LK.getScore() + 15); // Score between normal and fast zombie LK.getSound('hit').play(); return true; } return false; }; return self; }); var FastZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('fastZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; // Faster than normal zombie (2) self.health = 1; self.damage = 10; self.active = true; self.lastPlayerDistance = 0; self.update = function () { if (!self.active) return; var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate monster to face the player graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } var currentDistance = distance; if (self.lastPlayerDistance > 50 && currentDistance <= 50) { player.takeDamage(self.damage); self.active = false; } self.lastPlayerDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.active = false; // Create blood explosion with spreading effect for (var i = 0; i < 15; i++) { var bloodSplat = new BloodSplat(); // Start at monster position bloodSplat.x = self.x; bloodSplat.y = self.y; bloodSplat.graphics = bloodSplat.children[0]; var scale = 0.3 + Math.random() * 1.2; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; // Calculate random spread destination var angle = Math.random() * Math.PI * 2; var distance = 150 + Math.random() * 300; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; // Keep within game bounds targetX = Math.max(50, Math.min(1998, targetX)); targetY = Math.max(50, Math.min(2682, targetY)); // Animate spreading with random delay and duration var delay = Math.random() * 200; var duration = 300 + Math.random() * 400; tween(bloodSplat, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut }); bloodSplats.push(bloodSplat); game.addChild(bloodSplat); } LK.setScore(LK.getScore() + 10); LK.getSound('hit').play(); return true; } return false; }; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.health = 1; self.damage = 10; self.active = true; self.lastPlayerDistance = 0; self.update = function () { if (!self.active) return; var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate monster to face the player graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } var currentDistance = distance; if (self.lastPlayerDistance > 50 && currentDistance <= 50) { player.takeDamage(self.damage); self.active = false; } self.lastPlayerDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.active = false; // Create blood explosion with spreading effect for (var i = 0; i < 15; i++) { var bloodSplat = new BloodSplat(); // Start at monster position bloodSplat.x = self.x; bloodSplat.y = self.y; bloodSplat.graphics = bloodSplat.children[0]; var scale = 0.3 + Math.random() * 1.2; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; // Calculate random spread destination var angle = Math.random() * Math.PI * 2; var distance = 150 + Math.random() * 300; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; // Keep within game bounds targetX = Math.max(50, Math.min(1998, targetX)); targetY = Math.max(50, Math.min(2682, targetY)); // Animate spreading with random delay and duration var delay = Math.random() * 200; var duration = 300 + Math.random() * 400; tween(bloodSplat, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut }); bloodSplats.push(bloodSplat); game.addChild(bloodSplat); } LK.setScore(LK.getScore() + 10); LK.getSound('hit').play(); return true; } return false; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.fireRate = 30; // Slower initial fire rate self.fireTimer = 0; self.update = function () { if (self.fireTimer > 0) { self.fireTimer--; } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; LK.showGameOver(); } LK.effects.flashObject(self, 0xff0000, 300); }; self.canFire = function () { return self.fireTimer <= 0; }; self.fire = function () { if (self.canFire()) { self.fireTimer = self.fireRate; return true; } return false; }; return self; }); var ToughZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('toughZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; // Slower than normal zombie self.health = 2; // Much tougher self.damage = 15; // Higher damage self.active = true; self.lastPlayerDistance = 0; self.update = function () { if (!self.active) return; var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate monster to face the player graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } var currentDistance = distance; if (self.lastPlayerDistance > 50 && currentDistance <= 50) { player.takeDamage(self.damage); self.active = false; } self.lastPlayerDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.active = false; // Create blood explosion with spreading effect for (var i = 0; i < 15; i++) { var bloodSplat = new BloodSplat(); // Start at monster position bloodSplat.x = self.x; bloodSplat.y = self.y; bloodSplat.graphics = bloodSplat.children[0]; var scale = 0.3 + Math.random() * 1.2; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; // Calculate random spread destination var angle = Math.random() * Math.PI * 2; var distance = 150 + Math.random() * 300; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; // Keep within game bounds targetX = Math.max(50, Math.min(1998, targetX)); targetY = Math.max(50, Math.min(2682, targetY)); // Animate spreading with random delay and duration var delay = Math.random() * 200; var duration = 300 + Math.random() * 400; tween(bloodSplat, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut }); bloodSplats.push(bloodSplat); game.addChild(bloodSplat); } LK.setScore(LK.getScore() + 20); // Higher score for tough zombie LK.getSound('hit').play(); return true; } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ var player = null; var bullets = []; var monsters = []; var bloodSplats = []; var waveNumber = 1; var monstersInWave = 5; var monstersSpawned = 0; var monstersKilled = 0; var spawnTimer = 0; var waveDelay = 180; var bulletCooldown = 30; // Start with cooldown to prevent immediate shooting // Secret wave skip variables (hidden from normal players) var secretTapCount = 0; var secretTapTimer = 0; var secretZone = { x: 1900, y: 2600, width: 148, height: 132 }; // Bottom-right corner // Fast zombie spawn chance (starts at 10%, increases 1% per wave, max 80%) var fastZombieChance = 10; // Tough zombie spawn chance (starts at 5% after wave 5, increases 1% per wave, max 90%) var toughZombieChance = 5; // Crayz zombie spawn chance (10% after wave 15 to replace fast zombies) var crayzZombieChance = 10; // Card system variables var isCardSelectionActive = false; var cardContainer = null; var selectedPower = null; var availablePowers = ['bouncy', 'rapidfire', 'multishot']; var activePowers = []; var bouncyBulletCount = 1; // Number of bounces for bouncy bullets var multishotBulletCount = 1; // Number of bullets to fire for multishot var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var healthText = new Text2('Health: 100', { size: 50, fill: 0xFF4444 }); healthText.anchor.set(0, 0); healthText.x = 120; healthText.y = 20; LK.gui.topLeft.addChild(healthText); var waveText = new Text2('Wave: 1', { size: 50, fill: 0x4A90E2 }); waveText.anchor.set(1, 0); LK.gui.topRight.addChild(waveText); // Power utility functions function getPowerTitle(powerType) { switch (powerType) { case 'bouncy': return 'Bouncy Bullets'; case 'rapidfire': return 'Rapid Fire'; case 'multishot': return 'Multi Shot'; default: return 'Unknown Power'; } } function getPowerDescription(powerType) { switch (powerType) { case 'bouncy': return 'Bullets bounce to nearby enemies'; case 'rapidfire': return 'Faster shooting speed'; case 'multishot': return 'Shoot 3 bullets at once'; default: return 'Unknown effect'; } } function showCardSelection() { isCardSelectionActive = true; // Create card container cardContainer = new Container(); game.addChild(cardContainer); // Create dark overlay var overlay = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.8 }); overlay.tint = 0x000000; cardContainer.addChild(overlay); // Select 3 random powers var shuffledPowers = availablePowers.slice(); for (var i = shuffledPowers.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = shuffledPowers[i]; shuffledPowers[i] = shuffledPowers[j]; shuffledPowers[j] = temp; } var selectedPowers = shuffledPowers.slice(0, 3); // Create cards for (var i = 0; i < 3; i++) { var card = new Card(selectedPowers[i]); card.x = 512 + i * 512; card.y = 1366; cardContainer.addChild(card); // Animate cards in tween(card, { y: 1000 }, { duration: 500, easing: tween.easeOut }); } // Add title text var titleText = new Text2('Choose Your Power!', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 600; cardContainer.addChild(titleText); } function selectCard(powerType) { if (!isCardSelectionActive) return; // Add power to active powers activePowers.push(powerType); // Apply power effect applyPowerEffect(powerType); // Animate cards out and clean up if (cardContainer) { tween(cardContainer, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { if (cardContainer) { cardContainer.destroy(); } cardContainer = null; isCardSelectionActive = false; } }); } } function applyPowerEffect(powerType) { switch (powerType) { case 'rapidfire': player.fireRate = Math.max(1, Math.floor(player.fireRate / 2)); break; case 'multishot': // Increase bullet count for multishot multishotBulletCount++; break; case 'bouncy': // Increase bounce count for all future bullets bouncyBulletCount++; break; } } var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); player = game.addChild(new Player()); player.x = 1024; player.y = 1366; function spawnMonster() { if (monstersSpawned >= monstersInWave) return; // Spawn multiple monsters at once based on wave number - more balanced progression var monstersToSpawn = 1; if (waveNumber >= 3) monstersToSpawn = 2; if (waveNumber >= 6) monstersToSpawn = 3; if (waveNumber >= 10) monstersToSpawn = Math.min(4, Math.floor(waveNumber / 3)); var actualSpawn = Math.min(monstersToSpawn, monstersInWave - monstersSpawned); for (var spawnIndex = 0; spawnIndex < actualSpawn; spawnIndex++) { // Calculate current tough zombie chance (5% base + 1% per wave after wave 10, max 90%) var currentToughZombieChance = waveNumber >= 10 ? Math.min(5 + (waveNumber - 10), 90) : 0; // Determine zombie type with priority: tough > crayz > fast > normal var randomChance = Math.random() * 100; var monster; if (waveNumber >= 10 && randomChance < currentToughZombieChance) { monster = new ToughZombie(); } else if (waveNumber >= 15 && randomChance < currentToughZombieChance + crayzZombieChance) { // 10% chance to spawn crayz zombie instead of fast zombie after wave 15 monster = new CrayzZombie(); } else if (waveNumber >= 5 && randomChance < currentToughZombieChance + crayzZombieChance + fastZombieChance) { monster = new FastZombie(); } else { monster = new Monster(); } var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top monster.x = Math.random() * 2048; monster.y = -30; break; case 1: // Right monster.x = 2078; monster.y = Math.random() * 2732; break; case 2: // Bottom monster.x = Math.random() * 2048; monster.y = 2762; break; case 3: // Left monster.x = -30; monster.y = Math.random() * 2732; break; } // Speed is already set in the class constructors monsters.push(monster); game.addChild(monster); monstersSpawned++; } } function cleanupBullets() { for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (!bullet.active) { bullet.destroy(); bullets.splice(i, 1); } } } function cleanupMonsters() { for (var i = monsters.length - 1; i >= 0; i--) { var monster = monsters[i]; if (!monster.active) { monster.destroy(); monsters.splice(i, 1); monstersKilled++; } } } function cleanupBloodSplats() { for (var i = bloodSplats.length - 1; i >= 0; i--) { var bloodSplat = bloodSplats[i]; if (!bloodSplat.parent) { bloodSplats.splice(i, 1); } } } function checkBulletCollisions() { for (var i = 0; i < bullets.length; i++) { var bullet = bullets[i]; if (!bullet.active) continue; for (var j = 0; j < monsters.length; j++) { var monster = monsters[j]; if (!monster.active) continue; if (bullet.intersects(monster)) { monster.takeDamage(1); // Try to bounce bullet if bouncy power is active and bullet hasn't exceeded bounce limit if (bullet.bounceCount < bouncyBulletCount && activePowers.indexOf('bouncy') !== -1) { bullet.bounce(monster); // If bounce was successful (found a target), keep bullet active if (bullet.bounced) { break; // Stop checking other monsters for this bullet this frame } } // Only deactivate bullet if it reached bounce limit or bouncy power is not active if (bullet.bounceCount >= bouncyBulletCount || activePowers.indexOf('bouncy') === -1) { bullet.active = false; } break; } } } } function nextWave() { waveNumber++; // Check for card selection every 5 waves if (waveNumber % 5 === 0 && waveNumber > 0) { showCardSelection(); return; // Don't start next wave until card is selected } // More balanced wave size progression: slower growth after wave 10 if (waveNumber <= 10) { monstersInWave = 3 + waveNumber * 2; } else { monstersInWave = 23 + Math.floor((waveNumber - 10) / 2); } monstersSpawned = 0; monstersKilled = 0; spawnTimer = 0; waveDelay = 180; // Increase fast zombie chance by 1% each wave, max 80% fastZombieChance = Math.min(10 + waveNumber, 80); waveText.setText('Wave: ' + waveNumber); } game.down = function (x, y, obj) { // Secret wave skip check (bottom-right corner taps) if (x >= secretZone.x && x <= secretZone.x + secretZone.width && y >= secretZone.y && y <= secretZone.y + secretZone.height) { secretTapCount++; secretTapTimer = 120; // 2 seconds to complete sequence if (secretTapCount >= 5) { // Skip 3 waves var skipAmount = 3; // Skip waves instantly for (var skipCount = 0; skipCount < skipAmount; skipCount++) { waveNumber++; } // Calculate wave size using balanced progression if (waveNumber <= 10) { monstersInWave = 3 + waveNumber * 2; } else { monstersInWave = 23 + Math.floor((waveNumber - 10) / 2); } // Update fast zombie chance for the new wave fastZombieChance = Math.min(10 + waveNumber, 80); monstersSpawned = 0; monstersKilled = monstersInWave; // Clear all current monsters for (var i = 0; i < monsters.length; i++) { if (monsters[i].active) { monsters[i].active = false; } } secretTapCount = 0; waveText.setText('Wave: ' + waveNumber); // Visual feedback (brief flash) LK.effects.flashScreen(0x00ff00, 200); } return; } if (bulletCooldown <= 0 && player.fire()) { // Check if multishot is active var shotCount = activePowers.indexOf('multishot') !== -1 ? multishotBulletCount : 1; for (var shotIndex = 0; shotIndex < shotCount; shotIndex++) { var bullet = new Bullet(); bullet.x = player.x; bullet.y = player.y; if (shotCount === 1) { // Single shot - aim directly at target bullet.setDirection(x, y); } else { // Multishot - spread bullets var baseAngle = Math.atan2(y - player.y, x - player.x); var spreadAngle = (shotIndex - (shotCount - 1) / 2) * 0.3; // Center the spread var finalAngle = baseAngle + spreadAngle; var targetX = player.x + Math.cos(finalAngle) * 500; var targetY = player.y + Math.sin(finalAngle) * 500; bullet.setDirection(targetX, targetY); } bullets.push(bullet); game.addChild(bullet); } LK.getSound('shoot').play(); bulletCooldown = player.fireRate; } }; LK.playMusic('bgmusic'); game.update = function () { // Pause game during card selection if (isCardSelectionActive) { return; } // Secret wave skip timer if (secretTapTimer > 0) { secretTapTimer--; if (secretTapTimer <= 0) { secretTapCount = 0; // Reset if too slow } } if (bulletCooldown > 0) { bulletCooldown--; } if (waveDelay > 0) { waveDelay--; return; } spawnTimer++; if (spawnTimer >= 60 && monstersSpawned < monstersInWave) { spawnMonster(); spawnTimer = 0; } checkBulletCollisions(); cleanupBullets(); cleanupMonsters(); cleanupBloodSplats(); if (monstersKilled >= monstersInWave && monsters.length === 0) { nextWave(); } scoreText.setText('Score: ' + LK.getScore()); healthText.setText('Health: ' + player.health); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BloodSplat = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('blood', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 300; // 5 seconds at 60fps
self.fadeStartTime = 0;
self.isFading = false;
self.update = function () {
self.lifetime--;
// Start fading after 10 seconds
if (self.lifetime <= 0 && !self.isFading) {
self.isFading = true;
tween(graphics, {
alpha: 0
}, {
duration: 3000,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 20;
self.dx = 0;
self.dy = 0;
self.active = true;
self.bounced = false;
self.bounceCount = 0; // Track number of bounces performed
self.update = function () {
if (!self.active) return;
self.x += self.dx;
self.y += self.dy;
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.active = false;
}
};
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.dx = dx / distance * self.speed;
self.dy = dy / distance * self.speed;
// Rotate bullet to face direction of travel
graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
};
self.bounce = function (hitMonster) {
// Increment bounce count first
self.bounceCount++;
// Check if we've reached the bounce limit
if (self.bounceCount >= bouncyBulletCount) {
self.active = false;
return;
}
// Find nearest monster regardless of distance
var nearestMonster = null;
var nearestDistance = Infinity; // No distance limit
for (var i = 0; i < monsters.length; i++) {
var monster = monsters[i];
if (!monster.active || monster === hitMonster) continue;
var dx = monster.x - self.x;
var dy = monster.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestMonster = monster;
}
}
if (nearestMonster) {
self.bounced = true;
self.bounceTarget = nearestMonster; // Store the target
self.setDirection(nearestMonster.x, nearestMonster.y);
} else {
// No target found, deactivate bullet
self.active = false;
}
};
return self;
});
var Card = Container.expand(function (powerType) {
var self = Container.call(this);
// Card background
var cardBg = LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
cardBg.tint = 0x2c3e50;
self.addChild(cardBg);
// Card title text
var titleText = new Text2(getPowerTitle(powerType), {
size: 50,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -40;
self.addChild(titleText);
// Card description text
var descText = new Text2(getPowerDescription(powerType), {
size: 30,
fill: 0xECF0F1
});
descText.anchor.set(0.5, 0.5);
descText.y = 20;
self.addChild(descText);
self.powerType = powerType;
// Card selection handler
self.down = function (x, y, obj) {
selectCard(self.powerType);
};
return self;
});
var CrayzZombie = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('crayzZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5; // Faster speed for more challenge
self.health = 1;
self.damage = 12;
self.active = true;
self.lastPlayerDistance = 0;
self.spiralAngle = 0; // Angle for spiral movement
self.crazyTimer = 0; // Timer for random direction changes
self.suddenChangeX = 0; // Sudden movement offset X
self.suddenChangeY = 0; // Sudden movement offset Y
self.isChangingDirection = false; // Flag for direction change animation
// Start with a random crazy movement pattern immediately
self.startCrazyMovement = function () {
if (self.isChangingDirection) return;
self.isChangingDirection = true;
// Random sudden movement direction
var randomAngle = Math.random() * Math.PI * 2;
var randomDistance = 100 + Math.random() * 200;
var targetX = Math.cos(randomAngle) * randomDistance;
var targetY = Math.sin(randomAngle) * randomDistance;
// Animate sudden direction change
tween(self, {
suddenChangeX: targetX,
suddenChangeY: targetY
}, {
duration: 300 + Math.random() * 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Reset and prepare for next crazy movement
self.suddenChangeX = 0;
self.suddenChangeY = 0;
self.isChangingDirection = false;
}
});
};
self.update = function () {
if (!self.active) return;
// Screen center coordinates
var screenCenterX = 1024; // Half of 2048
var screenCenterY = 1366; // Half of 2732
// Calculate distance from screen center
var dxToCenter = screenCenterX - self.x;
var dyToCenter = screenCenterY - self.y;
var distanceToCenter = Math.sqrt(dxToCenter * dxToCenter + dyToCenter * dyToCenter);
// Calculate distance to player for damage detection
var dxToPlayer = player.x - self.x;
var dyToPlayer = player.y - self.y;
var distanceToPlayer = Math.sqrt(dxToPlayer * dxToPlayer + dyToPlayer * dyToPlayer);
// Spiral movement around screen center
self.spiralAngle += 0.1; // Spiral rotation speed
// Calculate current position relative to screen center
var currentAngle = Math.atan2(self.y - screenCenterY, self.x - screenCenterX);
var currentRadius = distanceToCenter;
// Gradually decrease radius to spiral inward
var targetRadius = Math.max(50, currentRadius - 1); // Minimum radius of 50 pixels
// Calculate new position on the spiral
var newAngle = currentAngle + 0.1; // Spiral by rotating around center
var newX = screenCenterX + Math.cos(newAngle) * targetRadius;
var newY = screenCenterY + Math.sin(newAngle) * targetRadius;
// Move toward the new spiral position
var dx = newX - self.x;
var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate zombie to face movement direction
graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
var currentDistance = distanceToPlayer;
if (self.lastPlayerDistance > 50 && currentDistance <= 50) {
player.takeDamage(self.damage);
self.active = false;
}
self.lastPlayerDistance = currentDistance;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.active = false;
// Create blood explosion with spreading effect
for (var i = 0; i < 15; i++) {
var bloodSplat = new BloodSplat();
// Start at monster position
bloodSplat.x = self.x;
bloodSplat.y = self.y;
bloodSplat.graphics = bloodSplat.children[0];
var scale = 0.3 + Math.random() * 1.2;
bloodSplat.graphics.scaleX = scale;
bloodSplat.graphics.scaleY = scale;
// Calculate random spread destination
var angle = Math.random() * Math.PI * 2;
var distance = 150 + Math.random() * 300;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
// Keep within game bounds
targetX = Math.max(50, Math.min(1998, targetX));
targetY = Math.max(50, Math.min(2682, targetY));
// Animate spreading with random delay and duration
var delay = Math.random() * 200;
var duration = 300 + Math.random() * 400;
tween(bloodSplat, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut
});
bloodSplats.push(bloodSplat);
game.addChild(bloodSplat);
}
LK.setScore(LK.getScore() + 15); // Score between normal and fast zombie
LK.getSound('hit').play();
return true;
}
return false;
};
return self;
});
var FastZombie = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('fastZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4; // Faster than normal zombie (2)
self.health = 1;
self.damage = 10;
self.active = true;
self.lastPlayerDistance = 0;
self.update = function () {
if (!self.active) return;
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate monster to face the player
graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
var currentDistance = distance;
if (self.lastPlayerDistance > 50 && currentDistance <= 50) {
player.takeDamage(self.damage);
self.active = false;
}
self.lastPlayerDistance = currentDistance;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.active = false;
// Create blood explosion with spreading effect
for (var i = 0; i < 15; i++) {
var bloodSplat = new BloodSplat();
// Start at monster position
bloodSplat.x = self.x;
bloodSplat.y = self.y;
bloodSplat.graphics = bloodSplat.children[0];
var scale = 0.3 + Math.random() * 1.2;
bloodSplat.graphics.scaleX = scale;
bloodSplat.graphics.scaleY = scale;
// Calculate random spread destination
var angle = Math.random() * Math.PI * 2;
var distance = 150 + Math.random() * 300;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
// Keep within game bounds
targetX = Math.max(50, Math.min(1998, targetX));
targetY = Math.max(50, Math.min(2682, targetY));
// Animate spreading with random delay and duration
var delay = Math.random() * 200;
var duration = 300 + Math.random() * 400;
tween(bloodSplat, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut
});
bloodSplats.push(bloodSplat);
game.addChild(bloodSplat);
}
LK.setScore(LK.getScore() + 10);
LK.getSound('hit').play();
return true;
}
return false;
};
return self;
});
var Monster = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.damage = 10;
self.active = true;
self.lastPlayerDistance = 0;
self.update = function () {
if (!self.active) return;
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate monster to face the player
graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
var currentDistance = distance;
if (self.lastPlayerDistance > 50 && currentDistance <= 50) {
player.takeDamage(self.damage);
self.active = false;
}
self.lastPlayerDistance = currentDistance;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.active = false;
// Create blood explosion with spreading effect
for (var i = 0; i < 15; i++) {
var bloodSplat = new BloodSplat();
// Start at monster position
bloodSplat.x = self.x;
bloodSplat.y = self.y;
bloodSplat.graphics = bloodSplat.children[0];
var scale = 0.3 + Math.random() * 1.2;
bloodSplat.graphics.scaleX = scale;
bloodSplat.graphics.scaleY = scale;
// Calculate random spread destination
var angle = Math.random() * Math.PI * 2;
var distance = 150 + Math.random() * 300;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
// Keep within game bounds
targetX = Math.max(50, Math.min(1998, targetX));
targetY = Math.max(50, Math.min(2682, targetY));
// Animate spreading with random delay and duration
var delay = Math.random() * 200;
var duration = 300 + Math.random() * 400;
tween(bloodSplat, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut
});
bloodSplats.push(bloodSplat);
game.addChild(bloodSplat);
}
LK.setScore(LK.getScore() + 10);
LK.getSound('hit').play();
return true;
}
return false;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.fireRate = 30; // Slower initial fire rate
self.fireTimer = 0;
self.update = function () {
if (self.fireTimer > 0) {
self.fireTimer--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xff0000, 300);
};
self.canFire = function () {
return self.fireTimer <= 0;
};
self.fire = function () {
if (self.canFire()) {
self.fireTimer = self.fireRate;
return true;
}
return false;
};
return self;
});
var ToughZombie = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('toughZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5; // Slower than normal zombie
self.health = 2; // Much tougher
self.damage = 15; // Higher damage
self.active = true;
self.lastPlayerDistance = 0;
self.update = function () {
if (!self.active) return;
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate monster to face the player
graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
var currentDistance = distance;
if (self.lastPlayerDistance > 50 && currentDistance <= 50) {
player.takeDamage(self.damage);
self.active = false;
}
self.lastPlayerDistance = currentDistance;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.active = false;
// Create blood explosion with spreading effect
for (var i = 0; i < 15; i++) {
var bloodSplat = new BloodSplat();
// Start at monster position
bloodSplat.x = self.x;
bloodSplat.y = self.y;
bloodSplat.graphics = bloodSplat.children[0];
var scale = 0.3 + Math.random() * 1.2;
bloodSplat.graphics.scaleX = scale;
bloodSplat.graphics.scaleY = scale;
// Calculate random spread destination
var angle = Math.random() * Math.PI * 2;
var distance = 150 + Math.random() * 300;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
// Keep within game bounds
targetX = Math.max(50, Math.min(1998, targetX));
targetY = Math.max(50, Math.min(2682, targetY));
// Animate spreading with random delay and duration
var delay = Math.random() * 200;
var duration = 300 + Math.random() * 400;
tween(bloodSplat, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut
});
bloodSplats.push(bloodSplat);
game.addChild(bloodSplat);
}
LK.setScore(LK.getScore() + 20); // Higher score for tough zombie
LK.getSound('hit').play();
return true;
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
var player = null;
var bullets = [];
var monsters = [];
var bloodSplats = [];
var waveNumber = 1;
var monstersInWave = 5;
var monstersSpawned = 0;
var monstersKilled = 0;
var spawnTimer = 0;
var waveDelay = 180;
var bulletCooldown = 30; // Start with cooldown to prevent immediate shooting
// Secret wave skip variables (hidden from normal players)
var secretTapCount = 0;
var secretTapTimer = 0;
var secretZone = {
x: 1900,
y: 2600,
width: 148,
height: 132
}; // Bottom-right corner
// Fast zombie spawn chance (starts at 10%, increases 1% per wave, max 80%)
var fastZombieChance = 10;
// Tough zombie spawn chance (starts at 5% after wave 5, increases 1% per wave, max 90%)
var toughZombieChance = 5;
// Crayz zombie spawn chance (10% after wave 15 to replace fast zombies)
var crayzZombieChance = 10;
// Card system variables
var isCardSelectionActive = false;
var cardContainer = null;
var selectedPower = null;
var availablePowers = ['bouncy', 'rapidfire', 'multishot'];
var activePowers = [];
var bouncyBulletCount = 1; // Number of bounces for bouncy bullets
var multishotBulletCount = 1; // Number of bullets to fire for multishot
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var healthText = new Text2('Health: 100', {
size: 50,
fill: 0xFF4444
});
healthText.anchor.set(0, 0);
healthText.x = 120;
healthText.y = 20;
LK.gui.topLeft.addChild(healthText);
var waveText = new Text2('Wave: 1', {
size: 50,
fill: 0x4A90E2
});
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
// Power utility functions
function getPowerTitle(powerType) {
switch (powerType) {
case 'bouncy':
return 'Bouncy Bullets';
case 'rapidfire':
return 'Rapid Fire';
case 'multishot':
return 'Multi Shot';
default:
return 'Unknown Power';
}
}
function getPowerDescription(powerType) {
switch (powerType) {
case 'bouncy':
return 'Bullets bounce to nearby enemies';
case 'rapidfire':
return 'Faster shooting speed';
case 'multishot':
return 'Shoot 3 bullets at once';
default:
return 'Unknown effect';
}
}
function showCardSelection() {
isCardSelectionActive = true;
// Create card container
cardContainer = new Container();
game.addChild(cardContainer);
// Create dark overlay
var overlay = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.8
});
overlay.tint = 0x000000;
cardContainer.addChild(overlay);
// Select 3 random powers
var shuffledPowers = availablePowers.slice();
for (var i = shuffledPowers.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = shuffledPowers[i];
shuffledPowers[i] = shuffledPowers[j];
shuffledPowers[j] = temp;
}
var selectedPowers = shuffledPowers.slice(0, 3);
// Create cards
for (var i = 0; i < 3; i++) {
var card = new Card(selectedPowers[i]);
card.x = 512 + i * 512;
card.y = 1366;
cardContainer.addChild(card);
// Animate cards in
tween(card, {
y: 1000
}, {
duration: 500,
easing: tween.easeOut
});
}
// Add title text
var titleText = new Text2('Choose Your Power!', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
cardContainer.addChild(titleText);
}
function selectCard(powerType) {
if (!isCardSelectionActive) return;
// Add power to active powers
activePowers.push(powerType);
// Apply power effect
applyPowerEffect(powerType);
// Animate cards out and clean up
if (cardContainer) {
tween(cardContainer, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
if (cardContainer) {
cardContainer.destroy();
}
cardContainer = null;
isCardSelectionActive = false;
}
});
}
}
function applyPowerEffect(powerType) {
switch (powerType) {
case 'rapidfire':
player.fireRate = Math.max(1, Math.floor(player.fireRate / 2));
break;
case 'multishot':
// Increase bullet count for multishot
multishotBulletCount++;
break;
case 'bouncy':
// Increase bounce count for all future bullets
bouncyBulletCount++;
break;
}
}
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
function spawnMonster() {
if (monstersSpawned >= monstersInWave) return;
// Spawn multiple monsters at once based on wave number - more balanced progression
var monstersToSpawn = 1;
if (waveNumber >= 3) monstersToSpawn = 2;
if (waveNumber >= 6) monstersToSpawn = 3;
if (waveNumber >= 10) monstersToSpawn = Math.min(4, Math.floor(waveNumber / 3));
var actualSpawn = Math.min(monstersToSpawn, monstersInWave - monstersSpawned);
for (var spawnIndex = 0; spawnIndex < actualSpawn; spawnIndex++) {
// Calculate current tough zombie chance (5% base + 1% per wave after wave 10, max 90%)
var currentToughZombieChance = waveNumber >= 10 ? Math.min(5 + (waveNumber - 10), 90) : 0;
// Determine zombie type with priority: tough > crayz > fast > normal
var randomChance = Math.random() * 100;
var monster;
if (waveNumber >= 10 && randomChance < currentToughZombieChance) {
monster = new ToughZombie();
} else if (waveNumber >= 15 && randomChance < currentToughZombieChance + crayzZombieChance) {
// 10% chance to spawn crayz zombie instead of fast zombie after wave 15
monster = new CrayzZombie();
} else if (waveNumber >= 5 && randomChance < currentToughZombieChance + crayzZombieChance + fastZombieChance) {
monster = new FastZombie();
} else {
monster = new Monster();
}
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
monster.x = Math.random() * 2048;
monster.y = -30;
break;
case 1:
// Right
monster.x = 2078;
monster.y = Math.random() * 2732;
break;
case 2:
// Bottom
monster.x = Math.random() * 2048;
monster.y = 2762;
break;
case 3:
// Left
monster.x = -30;
monster.y = Math.random() * 2732;
break;
}
// Speed is already set in the class constructors
monsters.push(monster);
game.addChild(monster);
monstersSpawned++;
}
}
function cleanupBullets() {
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet.active) {
bullet.destroy();
bullets.splice(i, 1);
}
}
}
function cleanupMonsters() {
for (var i = monsters.length - 1; i >= 0; i--) {
var monster = monsters[i];
if (!monster.active) {
monster.destroy();
monsters.splice(i, 1);
monstersKilled++;
}
}
}
function cleanupBloodSplats() {
for (var i = bloodSplats.length - 1; i >= 0; i--) {
var bloodSplat = bloodSplats[i];
if (!bloodSplat.parent) {
bloodSplats.splice(i, 1);
}
}
}
function checkBulletCollisions() {
for (var i = 0; i < bullets.length; i++) {
var bullet = bullets[i];
if (!bullet.active) continue;
for (var j = 0; j < monsters.length; j++) {
var monster = monsters[j];
if (!monster.active) continue;
if (bullet.intersects(monster)) {
monster.takeDamage(1);
// Try to bounce bullet if bouncy power is active and bullet hasn't exceeded bounce limit
if (bullet.bounceCount < bouncyBulletCount && activePowers.indexOf('bouncy') !== -1) {
bullet.bounce(monster);
// If bounce was successful (found a target), keep bullet active
if (bullet.bounced) {
break; // Stop checking other monsters for this bullet this frame
}
}
// Only deactivate bullet if it reached bounce limit or bouncy power is not active
if (bullet.bounceCount >= bouncyBulletCount || activePowers.indexOf('bouncy') === -1) {
bullet.active = false;
}
break;
}
}
}
}
function nextWave() {
waveNumber++;
// Check for card selection every 5 waves
if (waveNumber % 5 === 0 && waveNumber > 0) {
showCardSelection();
return; // Don't start next wave until card is selected
}
// More balanced wave size progression: slower growth after wave 10
if (waveNumber <= 10) {
monstersInWave = 3 + waveNumber * 2;
} else {
monstersInWave = 23 + Math.floor((waveNumber - 10) / 2);
}
monstersSpawned = 0;
monstersKilled = 0;
spawnTimer = 0;
waveDelay = 180;
// Increase fast zombie chance by 1% each wave, max 80%
fastZombieChance = Math.min(10 + waveNumber, 80);
waveText.setText('Wave: ' + waveNumber);
}
game.down = function (x, y, obj) {
// Secret wave skip check (bottom-right corner taps)
if (x >= secretZone.x && x <= secretZone.x + secretZone.width && y >= secretZone.y && y <= secretZone.y + secretZone.height) {
secretTapCount++;
secretTapTimer = 120; // 2 seconds to complete sequence
if (secretTapCount >= 5) {
// Skip 3 waves
var skipAmount = 3;
// Skip waves instantly
for (var skipCount = 0; skipCount < skipAmount; skipCount++) {
waveNumber++;
}
// Calculate wave size using balanced progression
if (waveNumber <= 10) {
monstersInWave = 3 + waveNumber * 2;
} else {
monstersInWave = 23 + Math.floor((waveNumber - 10) / 2);
}
// Update fast zombie chance for the new wave
fastZombieChance = Math.min(10 + waveNumber, 80);
monstersSpawned = 0;
monstersKilled = monstersInWave;
// Clear all current monsters
for (var i = 0; i < monsters.length; i++) {
if (monsters[i].active) {
monsters[i].active = false;
}
}
secretTapCount = 0;
waveText.setText('Wave: ' + waveNumber);
// Visual feedback (brief flash)
LK.effects.flashScreen(0x00ff00, 200);
}
return;
}
if (bulletCooldown <= 0 && player.fire()) {
// Check if multishot is active
var shotCount = activePowers.indexOf('multishot') !== -1 ? multishotBulletCount : 1;
for (var shotIndex = 0; shotIndex < shotCount; shotIndex++) {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
if (shotCount === 1) {
// Single shot - aim directly at target
bullet.setDirection(x, y);
} else {
// Multishot - spread bullets
var baseAngle = Math.atan2(y - player.y, x - player.x);
var spreadAngle = (shotIndex - (shotCount - 1) / 2) * 0.3; // Center the spread
var finalAngle = baseAngle + spreadAngle;
var targetX = player.x + Math.cos(finalAngle) * 500;
var targetY = player.y + Math.sin(finalAngle) * 500;
bullet.setDirection(targetX, targetY);
}
bullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
bulletCooldown = player.fireRate;
}
};
LK.playMusic('bgmusic');
game.update = function () {
// Pause game during card selection
if (isCardSelectionActive) {
return;
}
// Secret wave skip timer
if (secretTapTimer > 0) {
secretTapTimer--;
if (secretTapTimer <= 0) {
secretTapCount = 0; // Reset if too slow
}
}
if (bulletCooldown > 0) {
bulletCooldown--;
}
if (waveDelay > 0) {
waveDelay--;
return;
}
spawnTimer++;
if (spawnTimer >= 60 && monstersSpawned < monstersInWave) {
spawnMonster();
spawnTimer = 0;
}
checkBulletCollisions();
cleanupBullets();
cleanupMonsters();
cleanupBloodSplats();
if (monstersKilled >= monstersInWave && monsters.length === 0) {
nextWave();
}
scoreText.setText('Score: ' + LK.getScore());
healthText.setText('Health: ' + player.health);
};
Bullet. In-Game asset. 2d. High contrast. No shadows
A extremly basic zombie look from top. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A round drop of blood. In-Game asset. 2d. High contrast. No shadows
Make it simple no blood or object
Make its skin blue and make it smile
A happier version of this zombie but its will be purple
Make it green
small rock. In-Game asset. 2d. High contrast. No shadows
Yellow version of it
make it white
Explosion!. In-Game asset. 2d. High contrast. No shadows
Lightning. In-Game asset. 2d. High contrast. No shadows
Make it rainbow and extremly happy
A cowboy hat from top. In-Game asset. 2d. High contrast. No shadows
Hake his skin red and add horns
A card. In-Game asset. 2d. High contrast. No shadows