User prompt
DONT CHANGE THE BLOOD SPLATS
User prompt
game is lagging a lot. Fix it
User prompt
drop its change to replace any zombie from %10 to %0.05
User prompt
Create a assent for it
User prompt
Make it give 500 point and make it do everything 2 times better!
User prompt
Perfect! But make it do everything 2 times better
User prompt
Add all these powers to best zombie fully
User prompt
Give all these power of zombies to best zombie
User prompt
Add a new zombie type: Best zombie, it has all powers of all zombie types. It can replace any zombie with %10 change.
User prompt
Still not working spawn 100 crayz zombie at 99. wave
User prompt
100 crayz zombie doesnt spawn
User prompt
games end at 99. wave. Make it end when I bear 100 crayz zombie at the 100. wave
User prompt
when I press the left bottom corner. Make me go to 100 waves start or 99 waves end
User prompt
100. wave will be the final. At the 100. wave no zombies will spawn but only 100 5 times faster, crayz zombies.
User prompt
give me god mode (10 times damage, firerate, hp) when ı click to left bottom corner
User prompt
Its make me go to 99 wave then the 101 wave
User prompt
When I use the wave skipper. Make me go to 100. waves start
User prompt
Dont let the secret wave skipper to skip the 98 wave
User prompt
Dont let the secret wave skipper skip the 99. wave
User prompt
Make a AMAZİNG boss fight for 100 wave. And also dont make any weather appear there ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Change time dilations effect to this : When zombies get close to player they got slowed down by %10 ( Every time player picks another time dilations add %10 to slowness)
User prompt
Make the lucky shoots lucky bullets that deals double damage look pink ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Lucky shoots lucky bullets are not pink make them pink
User prompt
add a sound effect and texture for lightning
User prompt
Add a effect for the shield card ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * 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.piercedEnemies = []; // Track pierced enemies for piercing rounds self.targetX = 0; // Target position for magnetic bullets self.targetY = 0; self.frozenEnemies = []; // Track frozen enemies for freeze shot self.ricochetCount = 0; // Track number of ricochets self.isLuckyShot = false; // Track if this is a lucky shot self.update = function () { if (!self.active) return; // Apply pink tint for lucky shot bullets if (self.isLuckyShot) { graphics.tint = 0xff69b4; // Pink color } // Apply magnetic bullets homing if active if (magneticBulletsStacks > 0 && self.active) { var nearestMonster = null; var nearestDistance = 300 + magneticBulletsStacks * 50; // Increased range per stack for (var i = 0; i < monsters.length; i++) { var monster = monsters[i]; if (!monster.active) 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) { // Gradually adjust direction toward target var homingStrength = 0.1 + magneticBulletsStacks * 0.05; // Stronger homing per stack var targetDx = nearestMonster.x - self.x; var targetDy = nearestMonster.y - self.y; var targetDistance = Math.sqrt(targetDx * targetDx + targetDy * targetDy); if (targetDistance > 0) { var targetUnitX = targetDx / targetDistance; var targetUnitY = targetDy / targetDistance; self.dx = self.dx * (1 - homingStrength) + targetUnitX * self.speed * homingStrength; self.dy = self.dy * (1 - homingStrength) + targetUnitY * self.speed * homingStrength; } } } self.x += self.dx; self.y += self.dy; // Check for ricochet off walls if (ricochetMasterStacks > 0 && self.ricochetCount < ricochetMasterStacks) { var ricochetChance = Math.min(ricochetMasterStacks * 25, 100); // 25% per stack, max 100% var shouldRicochet = Math.random() * 100 < ricochetChance; if (shouldRicochet && (self.x <= 0 || self.x >= 2048 || self.y <= 0 || self.y >= 2732)) { self.ricochetCount++; // Bounce off walls if (self.x <= 0 || self.x >= 2048) { self.dx = -self.dx; self.x = Math.max(0, Math.min(2048, self.x)); } if (self.y <= 0 || self.y >= 2732) { self.dy = -self.dy; self.y = Math.max(0, Math.min(2732, self.y)); } // Find nearest enemy to ricochet toward var nearestMonster = null; var nearestDistance = Infinity; for (var i = 0; i < monsters.length; i++) { var monster = monsters[i]; if (!monster.active) 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.setDirection(nearestMonster.x, nearestMonster.y); } } else if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.active = false; } } else 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 = 25; // Even faster speed for maximum challenge self.baseSpeed = 25; // Store original speed for time dilation 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; // Apply time dilation effect when close to player var currentSpeed = self.baseSpeed; if (timeDilationStacks > 0 && distanceToPlayer <= 300) { // Within 300 pixels of player var slowdownPercent = timeDilationStacks * 10; // 10% per stack var slowdownMultiplier = 1 - slowdownPercent / 100; currentSpeed = self.baseSpeed * slowdownMultiplier; } // 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 * currentSpeed; self.y += dy / distance * currentSpeed; // Rotate zombie to face movement direction graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } var currentDistance = distanceToPlayer; // CrayzZombie can deal damage when getting close to screen center if (distanceToCenter <= 250) { player.takeDamage(self.damage); self.active = false; } self.lastPlayerDistance = distanceToPlayer; }; 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.baseSpeed = 4; // Store original speed for time dilation 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); // Apply time dilation effect when close to player var currentSpeed = self.baseSpeed; if (timeDilationStacks > 0 && distance <= 300) { // Within 300 pixels of player var slowdownPercent = timeDilationStacks * 10; // 10% per stack var slowdownMultiplier = 1 - slowdownPercent / 100; currentSpeed = self.baseSpeed * slowdownMultiplier; } if (distance > 0) { self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; // 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 FrozenMonster = Container.expand(function (originalMonster) { var self = Container.call(this); self.originalMonster = originalMonster; self.originalSpeed = originalMonster.speed; self.freezeDuration = 180 + freezeShotStacks * 60; // 3 seconds base + 1 second per extra stack self.update = function () { if (self.freezeDuration > 0) { self.freezeDuration--; self.originalMonster.speed = self.originalSpeed * 0.5; // 50% slower // Visual effect - blue tint if (!self.originalMonster.children[0].tintApplied) { self.originalMonster.children[0].tint = 0x4444ff; self.originalMonster.children[0].tintApplied = true; } } else { // Restore original speed and remove tint self.originalMonster.speed = self.originalSpeed; if (self.originalMonster.children[0].tintApplied) { self.originalMonster.children[0].tint = 0xffffff; self.originalMonster.children[0].tintApplied = false; } self.destroy(); } }; return self; }); var MegaSplitterZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('splitterZombie', { anchorX: 0.5, anchorY: 0.5 }); // Green tint to distinguish from regular splitter zombie graphics.tint = 0x44aa44; self.speed = 2; // Same speed as splitter zombie self.health = 4; // Double HP of splitter zombie (2 * 2) self.damage = 12; // Same damage as splitter zombie 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 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); } // Spawn 2 splitter zombies for (var s = 0; s < 2; s++) { var splitterZombie = new SplitterZombie(); // Position splitter zombies around the original position var spawnAngle = Math.PI * s + Math.random() * 0.5; var spawnDistance = 100 + Math.random() * 50; splitterZombie.x = self.x + Math.cos(spawnAngle) * spawnDistance; splitterZombie.y = self.y + Math.sin(spawnAngle) * spawnDistance; // Keep within bounds splitterZombie.x = Math.max(125, Math.min(1923, splitterZombie.x)); splitterZombie.y = Math.max(125, Math.min(2607, splitterZombie.y)); monsters.push(splitterZombie); game.addChild(splitterZombie); } LK.setScore(LK.getScore() + 35); // Higher score for mega splitter zombie LK.getSound('hit').play(); return true; } return false; }; return self; }); var MiniZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); // Scale down to make it smaller graphics.scaleX = 0.5; graphics.scaleY = 0.5; // Tint it green to distinguish from normal zombies graphics.tint = 0x44aa44; self.speed = 5; // Fast movement self.health = 1; self.damage = 8; 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 smaller blood explosion for (var i = 0; i < 8; 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.2 + Math.random() * 0.8; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; // Calculate random spread destination var angle = Math.random() * Math.PI * 2; var distance = 80 + Math.random() * 150; 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 = 200 + Math.random() * 300; tween(bloodSplat, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut }); bloodSplats.push(bloodSplat); game.addChild(bloodSplat); } LK.setScore(LK.getScore() + 5); // Lower score for mini zombie 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.baseSpeed = 2; // Store original speed for time dilation 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); // Apply time dilation effect when close to player var currentSpeed = self.baseSpeed; if (timeDilationStacks > 0 && distance <= 300) { // Within 300 pixels of player var slowdownPercent = timeDilationStacks * 10; // 10% per stack var slowdownMultiplier = 1 - slowdownPercent / 100; currentSpeed = self.baseSpeed * slowdownMultiplier; } if (distance > 0) { self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; // 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--; } if (shieldCooldown > 0) { shieldCooldown--; } // Shield visual effect when available if (shieldGeneratorStacks > 0 && shieldCooldown <= 0) { // Create pulsing blue glow effect when shield is ready var pulseIntensity = Math.sin(LK.ticks * 0.1) * 0.3 + 0.7; // Pulse between 0.4 and 1.0 graphics.tint = 0x4488ff; // Blue tint graphics.alpha = pulseIntensity; } else { // Normal appearance when no shield or shield on cooldown graphics.tint = 0xffffff; // White (normal) graphics.alpha = 1.0; } }; self.takeDamage = function (damage) { // Check for shield protection if (shieldGeneratorStacks > 0 && shieldCooldown <= 0) { // Shield absorbs the attack shieldCooldown = Math.max(180, 900 - (shieldGeneratorStacks - 1) * 120); // 15 seconds base, -2 seconds per extra stack, min 3 seconds LK.effects.flashObject(self, 0x0088ff, 300); // Blue flash for shield return; // No damage taken } // Apply Thick Skin damage reduction var reducedDamage = Math.max(1, damage - thickSkinStacks); self.health -= reducedDamage; // Activate Boom Time explosion if we have stacks if (boomTimeStacks > 0) { var explosionDamage = boomTimeStacks; // 1 damage per stack var explosionRadius = 150 * (1 + (boomTimeStacks - 1)); // 150 base radius, doubled for each additional stack // Create explosion visual effect var explosionVisual = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.8 }); var explosionScale = explosionRadius / 150 * (1 + (boomTimeStacks - 1)); // Scale based on radius and stacks - 2x bigger per additional stack explosionVisual.scaleX = explosionScale; explosionVisual.scaleY = explosionScale; game.addChild(explosionVisual); // Animate explosion tween(explosionVisual, { scaleX: explosionScale * 1.5, scaleY: explosionScale * 1.5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosionVisual.destroy(); } }); // Play explosion sound LK.getSound('explosion').play(); // Damage nearby monsters for (var i = 0; i < monsters.length; i++) { var monster = monsters[i]; if (!monster.active) continue; var dx = monster.x - self.x; var dy = monster.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= explosionRadius) { monster.takeDamage(explosionDamage); } } } 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 Rock = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('rock', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.dx = 0; self.dy = 0; self.active = true; self.damage = 5; self.update = function () { if (!self.active) return; self.x += self.dx; self.y += self.dy; // Remove rock if it goes off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.active = false; } // Check collision with player var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 85) { // Player radius + rock radius player.takeDamage(self.damage); 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 rock to face direction of travel graphics.rotation = Math.atan2(dy, dx); } }; return self; }); var RockThrowerZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('rockThrowerZombie', { anchorX: 0.5, anchorY: 0.5 }); graphics.rotation = Math.PI; // Face downward like other zombies self.speed = 1; // Slower movement speed self.health = 1; self.damage = 0; // Doesn't deal contact damage self.active = true; self.lastPlayerDistance = 0; self.throwCooldown = 0; self.throwRange = 900; // Range to start throwing rocks self.isInRange = false; 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); // Move toward player but stop at throwing range if (distance > self.throwRange) { if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate zombie to face the player graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } self.isInRange = false; } else { // In range - stop moving and start throwing self.isInRange = true; // Face the player graphics.rotation = Math.atan2(dy, dx) + Math.PI / 2; } // Throw rocks when in range if (self.isInRange && self.throwCooldown <= 0) { self.throwRock(); self.throwCooldown = 120; // 2 seconds between throws } if (self.throwCooldown > 0) { self.throwCooldown--; } self.lastPlayerDistance = distance; }; self.throwRock = function () { var rock = new Rock(); rock.x = self.x; rock.y = self.y; rock.setDirection(player.x, player.y); rocks.push(rock); game.addChild(rock); // Shake animation when throwing var originalX = self.x; var originalY = self.y; tween(self, { x: originalX + 15, y: originalY + 10 }, { duration: 80, onFinish: function onFinish() { tween(self, { x: originalX - 10, y: originalY - 8 }, { duration: 80, onFinish: function onFinish() { tween(self, { x: originalX, y: originalY }, { duration: 80 }); } }); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.active = false; // Create blood explosion for (var i = 0; i < 12; i++) { var bloodSplat = new BloodSplat(); bloodSplat.x = self.x; bloodSplat.y = self.y; bloodSplat.graphics = bloodSplat.children[0]; var scale = 0.3 + Math.random() * 1.0; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; var angle = Math.random() * Math.PI * 2; var distance = 120 + Math.random() * 250; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; targetX = Math.max(50, Math.min(1998, targetX)); targetY = Math.max(50, Math.min(2682, targetY)); 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() + 18); // Score between tough and splitter LK.getSound('hit').play(); return true; } return false; }; return self; }); var SplitterZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('splitterZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; // Medium speed self.health = 2; // Same as tough zombie self.damage = 12; // Medium 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 for (var i = 0; i < 12; 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.0; bloodSplat.graphics.scaleX = scale; bloodSplat.graphics.scaleY = scale; // Calculate random spread destination var angle = Math.random() * Math.PI * 2; var distance = 120 + Math.random() * 250; 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); } // Spawn 2-3 mini zombies var miniCount = 2 + Math.floor(Math.random() * 2); // 2 or 3 mini zombies for (var m = 0; m < miniCount; m++) { var miniZombie = new MiniZombie(); // Position mini zombies around the original position var spawnAngle = Math.PI * 2 / miniCount * m + Math.random() * 0.5; var spawnDistance = 80 + Math.random() * 40; miniZombie.x = self.x + Math.cos(spawnAngle) * spawnDistance; miniZombie.y = self.y + Math.sin(spawnAngle) * spawnDistance; // Keep within bounds miniZombie.x = Math.max(125, Math.min(1923, miniZombie.x)); miniZombie.y = Math.max(125, Math.min(2607, miniZombie.y)); monsters.push(miniZombie); game.addChild(miniZombie); } LK.setScore(LK.getScore() + 25); // Higher score for splitter zombie LK.getSound('hit').play(); 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.baseSpeed = 1.5; // Store original speed for time dilation 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); // Apply time dilation effect when close to player var currentSpeed = self.baseSpeed; if (timeDilationStacks > 0 && distance <= 300) { // Within 300 pixels of player var slowdownPercent = timeDilationStacks * 10; // 10% per stack var slowdownMultiplier = 1 - slowdownPercent / 100; currentSpeed = self.baseSpeed * slowdownMultiplier; } if (distance > 0) { self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; // 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; }); var TransparentZombie = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('transparentZombie', { anchorX: 0.5, anchorY: 0.5 }); graphics.alpha = 0.05; // 95% transparent (5% opacity) self.speed = 4; // Same speed as fast zombie 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]; bloodSplat.graphics.alpha = 0.5; // Make blood 50% transparent 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() + 12); // Score between fast and crayz 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 rocks = []; 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; // Splitter zombie spawn chance (15% after wave 18 to replace tough zombies) var splitterZombieChance = 15; // Mega splitter zombie spawn chance (20% after wave 30 to replace splitter zombies) var megaSplitterZombieChance = 20; // Rock thrower zombie spawn chance (10% after wave 25 to replace normal zombies) var rockThrowerZombieChance = 10; // Weather system variables var currentWeather = null; var weatherActive = false; var weatherChance = 5; // Starting at 5% after wave 10 var weatherText = null; var weatherTypes = ['windy', 'bloodSmell', 'bloodRain', 'unluckyClouds']; var isExtraSpecialWeather = false; var weatherEffects = { windy: false, bloodSmell: false, bloodRain: false, unluckyClouds: false, bloodMoon: false, "void": false }; // Weather utility functions function getWeatherName(weather) { var prefix = isExtraSpecialWeather ? 'EXTRA ' : ''; switch (weather) { case 'windy': return prefix + 'WINDY'; case 'bloodSmell': return prefix + 'BLOOD SMELL'; case 'bloodRain': return prefix + 'BLOOD RAIN'; case 'unluckyClouds': return prefix + 'UNLUCKY CLOUDS'; case 'bloodMoon': return prefix + 'BLOOD MOON'; case 'void': return 'VOID'; default: return ''; } } function getWeatherDescription(weather) { var multiplier = isExtraSpecialWeather ? 2 : 1; switch (weather) { case 'windy': return isExtraSpecialWeather ? 'All zombies are 100% faster' : 'All zombies are 50% faster'; case 'bloodSmell': return isExtraSpecialWeather ? 'Quadruple amount of zombies' : 'Double amount of zombies'; case 'bloodRain': return isExtraSpecialWeather ? 'Every zombie has 4x HP' : 'Every zombie has double HP'; case 'unluckyClouds': return isExtraSpecialWeather ? '40% chance your gun won\'t shoot' : '20% chance your gun won\'t shoot'; case 'bloodMoon': return isExtraSpecialWeather ? 'ALL EXTRA EFFECTS COMBINED!' : 'ALL EFFECTS COMBINED!'; case 'void': return 'ALL EFFECTS WITH 10X INTENSITY!'; default: return ''; } } function applyWeatherEffects(weather) { // Reset all weather effects first weatherEffects.windy = false; weatherEffects.bloodSmell = false; weatherEffects.bloodRain = false; weatherEffects.unluckyClouds = false; weatherEffects.bloodMoon = false; weatherEffects["void"] = false; if (weather === 'void') { // VOID activates all effects with extreme intensity weatherEffects.windy = true; weatherEffects.bloodSmell = true; weatherEffects.bloodRain = true; weatherEffects.unluckyClouds = true; weatherEffects["void"] = true; } else if (weather === 'bloodMoon') { // Blood moon activates all effects weatherEffects.windy = true; weatherEffects.bloodSmell = true; weatherEffects.bloodRain = true; weatherEffects.unluckyClouds = true; weatherEffects.bloodMoon = true; } else if (weather) { weatherEffects[weather] = true; } } function showWeatherAnnouncement(weather) { var weatherName = getWeatherName(weather); var weatherDescription = getWeatherDescription(weather); // Determine color based on weather type and if it's extra special var textColor = 0xFFFFFF; // Default white if (weather === 'void') { textColor = 0x8A2BE2; // Purple for VOID weather } else if (weather === 'bloodMoon') { if (isExtraSpecialWeather) { textColor = 0x8B0000; // Dark red for extra special blood moon } else { textColor = 0xFF0000; // Red for normal blood moon } } else if (isExtraSpecialWeather) { textColor = 0xFFD700; // Golden for extra special weather } // Create weather text at center of screen weatherText = new Text2(weatherName + '\n' + weatherDescription, { size: 120, fill: textColor }); weatherText.anchor.set(0.5, 0.5); weatherText.x = 1024; weatherText.y = 1366; game.addChild(weatherText); // After 3 seconds, shrink and move to bottom middle LK.setTimeout(function () { if (weatherText) { // Change text to only show weather name (remove description) weatherText.setText(weatherName); tween(weatherText, { scaleX: 0.3, scaleY: 0.3, x: 1024, y: 2650 }, { duration: 1000, easing: tween.easeInOut }); } }, 3000); } function checkForWeather() { // Only check for weather after wave 10 if (waveNumber < 10) return; // Calculate current weather chance (5% + 2.5% per wave, max 100%) var currentWeatherChance = Math.min(5 + (waveNumber - 10) * 2.5, 100); if (Math.random() * 100 < currentWeatherChance) { var selectedWeather; // Reset extra special weather flag isExtraSpecialWeather = false; // When weather chance is 100%, check for extra special weather if (currentWeatherChance >= 100) { // Calculate extra special weather chance (30% base + 5% per wave after reaching 100%) // Wave when weather chance first reaches 100% is approximately wave 46: 5 + (46-10) * 2.5 = 95, next wave is 97.5, next is 100 var waveWhenMaxReached = 46; // Approximate wave when weather chance reaches 100% var extraSpecialChance = 30; if (waveNumber > waveWhenMaxReached) { extraSpecialChance = 30 + (waveNumber - waveWhenMaxReached) * 5; } if (Math.random() * 100 < extraSpecialChance) { isExtraSpecialWeather = true; } } // Extremely rare chance for VOID weather (0.1% chance independent of other weather) if (Math.random() * 1000 < 1) { selectedWeather = 'void'; } else if (Math.random() * 100 < 1) { // Very small chance for blood moon (1% of weather events) selectedWeather = 'bloodMoon'; } else { // Equal chance for other weather types selectedWeather = weatherTypes[Math.floor(Math.random() * weatherTypes.length)]; } currentWeather = selectedWeather; weatherActive = true; applyWeatherEffects(selectedWeather); showWeatherAnnouncement(selectedWeather); } } function clearWeather() { currentWeather = null; weatherActive = false; isExtraSpecialWeather = false; weatherEffects["void"] = false; applyWeatherEffects(null); // Remove weather text if it exists if (weatherText) { weatherText.destroy(); weatherText = null; } } // Card system variables var isCardSelectionActive = false; var cardContainer = null; var selectedPower = null; var availablePowers = ['bouncy', 'rapidfire', 'multishot', 'freshMeat', 'thickSkin', 'hotBullets', 'vampireBite', 'boomTime', 'shieldGenerator', 'piercingRounds', 'luckyShot', 'magneticBullets', 'chainLightning', 'freezeShot', 'ricochetMaster', 'timeDilation']; var activePowers = []; var bouncyBulletCount = 1; // Number of bounces for bouncy bullets var multishotBulletCount = 1; // Number of bullets to fire for multishot var freshMeatStacks = 0; // Number of Fresh Meat cards collected var thickSkinStacks = 0; // Number of Thick Skin cards collected for damage reduction var hotBulletStacks = 0; // Number of Hot Bullets cards collected for extra damage var vampireBiteStacks = 0; // Number of Vampire Bite cards collected var vampireBiteKillCount = 0; // Track kills for vampire bite healing var boomTimeStacks = 0; // Number of Boom Time cards collected var shieldGeneratorStacks = 0; // Number of Shield Generator cards collected var shieldCooldown = 0; // Cooldown timer for shield var piercingRoundsStacks = 0; // Number of Piercing Rounds cards collected var luckyShotStacks = 0; // Number of Lucky Shot cards collected var magneticBulletsStacks = 0; // Number of Magnetic Bullets cards collected var chainLightningStacks = 0; // Number of Chain Lightning cards collected var freezeShotStacks = 0; // Number of Freeze Shot cards collected var ricochetMasterStacks = 0; // Number of Ricochet Master cards collected var timeDilationStacks = 0; // Number of Time Dilation cards collected var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 120; scoreText.y = 80; LK.gui.topLeft.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(0, 0); waveText.x = 120; waveText.y = 142; LK.gui.topLeft.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'; case 'freshMeat': return 'Fresh Meat'; case 'thickSkin': return 'Thick Skin'; case 'hotBullets': return 'Hot Bullets'; case 'vampireBite': return 'Vampire Bite'; case 'boomTime': return 'Boom Time!'; case 'shieldGenerator': return 'Shield Generator'; case 'piercingRounds': return 'Piercing Rounds'; case 'luckyShot': return 'Lucky Shot'; case 'magneticBullets': return 'Magnetic Bullets'; case 'chainLightning': return 'Chain Lightning'; case 'freezeShot': return 'Freeze Shot'; case 'ricochetMaster': return 'Ricochet Master'; case 'timeDilation': return 'Time Dilation'; 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'; case 'freshMeat': return 'Regenerate 5 HP after each wave'; case 'thickSkin': return 'Reduce all damage taken by 1'; case 'hotBullets': return 'Bullets deal +1 damage'; case 'vampireBite': return 'Heal 1 HP for every 10 enemies killed'; case 'boomTime': return 'Explosion damages nearby zombies when hurt'; case 'shieldGenerator': return 'Absorb next attack every 15 seconds'; case 'piercingRounds': return 'Bullets pass through +1 enemy'; case 'luckyShot': return '10% chance for double damage bullets'; case 'magneticBullets': return 'Bullets home in on nearby enemies'; case 'chainLightning': return '20% chance to instantly kill nearby enemy'; case 'freezeShot': return '15% chance to slow hit enemies'; case 'ricochetMaster': return '25% chance bullets ricochet off walls'; case 'timeDilation': return 'Enemies move 10% slower when close to you'; 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; case 'freshMeat': // Increase Fresh Meat stacks for HP regeneration freshMeatStacks++; break; case 'thickSkin': // Increase damage reduction stacks thickSkinStacks++; break; case 'hotBullets': // Increase bullet damage stacks hotBulletStacks++; break; case 'vampireBite': // Increase vampire bite stacks vampireBiteStacks++; break; case 'boomTime': // Increase boom time stacks boomTimeStacks++; break; case 'shieldGenerator': shieldGeneratorStacks++; break; case 'piercingRounds': piercingRoundsStacks++; break; case 'luckyShot': luckyShotStacks++; break; case 'magneticBullets': magneticBulletsStacks++; break; case 'chainLightning': chainLightningStacks++; break; case 'freezeShot': freezeShotStacks++; break; case 'ricochetMaster': ricochetMasterStacks++; break; case 'timeDilation': timeDilationStacks++; 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; // Wave 100 special spawning - only 5x faster CrayzZombies if (waveNumber === 100) { // Spawn 10 CrayzZombies at a time for wave 100 var monstersToSpawn = Math.min(10, monstersInWave - monstersSpawned); for (var spawnIndex = 0; spawnIndex < monstersToSpawn; spawnIndex++) { var monster = new CrayzZombie(); // Make them 5x faster monster.speed = monster.baseSpeed * 5; monster.baseSpeed = monster.baseSpeed * 5; // Spawn from all sides 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; } monsters.push(monster); game.addChild(monster); monstersSpawned++; } return; } // Normal spawning for waves 1-99 // 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: splitter > tough > crayz > fast > rockthrower > normal var randomChance = Math.random() * 100; var monster; if (waveNumber >= 10 && randomChance < currentToughZombieChance) { // 30% chance to spawn splitter zombie instead of tough zombie after wave 25 if (waveNumber >= 25 && Math.random() * 100 < 30) { // 20% chance to spawn mega splitter zombie instead of splitter zombie after wave 30 if (waveNumber >= 30 && Math.random() * 100 < megaSplitterZombieChance) { monster = new MegaSplitterZombie(); } else { monster = new SplitterZombie(); } } else { 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) { // 5% chance to spawn transparent zombie instead of fast zombie after wave 21 if (waveNumber >= 21 && Math.random() * 100 < 5) { monster = new TransparentZombie(); } else { monster = new FastZombie(); } } else if (waveNumber >= 25 && randomChance < currentToughZombieChance + crayzZombieChance + fastZombieChance + rockThrowerZombieChance) { // 10% chance to spawn rock thrower zombie after wave 25 monster = new RockThrowerZombie(); } 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; } // Apply weather effects to monster if (weatherEffects.windy) { if (weatherEffects["void"]) { monster.speed *= 11; // 10x effect (1000% faster) } else { monster.speed *= isExtraSpecialWeather ? 2.0 : 1.5; // 100% faster for extra, 50% for normal } } if (weatherEffects.bloodRain) { if (weatherEffects["void"]) { monster.health *= 20; // 10x effect (2000% HP) } else { monster.health *= isExtraSpecialWeather ? 4 : 2; // 4x HP for extra, 2x for normal } } 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 cleanupRocks() { for (var i = rocks.length - 1; i >= 0; i--) { var rock = rocks[i]; if (!rock.active) { rock.destroy(); rocks.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)) { // Skip if this enemy was already pierced by this bullet if (bullet.piercedEnemies.indexOf(monster) !== -1) { continue; } // Calculate damage with Hot Bullets and Lucky Shot bonus var bulletDamage = 1 + hotBulletStacks; if (luckyShotStacks > 0) { var luckyChance = Math.min(luckyShotStacks * 10, 50); // 10% per stack, max 50% if (Math.random() * 100 < luckyChance) { bulletDamage *= 2; // Double damage bullet.isLuckyShot = true; // Visual effect for lucky shot LK.effects.flashObject(monster, 0xffff00, 200); // Yellow flash } } var monsterKilled = monster.takeDamage(bulletDamage); // Apply freeze shot effect if (freezeShotStacks > 0 && bullet.frozenEnemies.indexOf(monster) === -1) { var freezeChance = Math.min(freezeShotStacks * 15, 60); // 15% per stack, max 60% if (Math.random() * 100 < freezeChance) { var frozenMonster = new FrozenMonster(monster); game.addChild(frozenMonster); bullet.frozenEnemies.push(monster); } } // Apply chain lightning effect if (chainLightningStacks > 0 && monsterKilled) { var chainChance = Math.min(chainLightningStacks * 20, 80); // 20% per stack, max 80% if (Math.random() * 100 < chainChance) { // Find nearest monster to chain to var nearestChainMonster = null; var nearestChainDistance = 200; // Chain range for (var k = 0; k < monsters.length; k++) { var chainMonster = monsters[k]; if (!chainMonster.active || chainMonster === monster) continue; var dx = chainMonster.x - monster.x; var dy = chainMonster.y - monster.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < nearestChainDistance) { nearestChainDistance = distance; nearestChainMonster = chainMonster; } } if (nearestChainMonster) { // Create lightning bolt visual effect var lightningBolt = LK.getAsset('lightning', { anchorX: 0.5, anchorY: 0.5, x: (monster.x + nearestChainMonster.x) / 2, y: (monster.y + nearestChainMonster.y) / 2, alpha: 0.9 }); // Calculate rotation to point from monster to chained monster var lightningDx = nearestChainMonster.x - monster.x; var lightningDy = nearestChainMonster.y - monster.y; lightningBolt.rotation = Math.atan2(lightningDy, lightningDx) + Math.PI / 2; game.addChild(lightningBolt); // Animate lightning bolt tween(lightningBolt, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { lightningBolt.destroy(); } }); // Play lightning sound LK.getSound('lightning').play(); nearestChainMonster.takeDamage(999); // Instant kill // Visual lightning effect LK.effects.flashObject(nearestChainMonster, 0x00ffff, 300); // Cyan flash } } } // Check for Vampire Bite healing if (monsterKilled && vampireBiteStacks > 0) { vampireBiteKillCount++; var healsNeeded = Math.max(1, 10 - (vampireBiteStacks - 1)); // 10 kills for first stack, -1 for each additional (min 1) if (vampireBiteKillCount >= healsNeeded) { vampireBiteKillCount = 0; if (player.health < 100) { player.health = Math.min(100, player.health + 1); } } } // Add monster to pierced list for piercing rounds if (piercingRoundsStacks > 0) { bullet.piercedEnemies.push(monster); // Check if bullet has pierced maximum number of enemies if (bullet.piercedEnemies.length > piercingRoundsStacks) { bullet.active = false; break; } // Continue to next monster for piercing continue; } // 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++; // Apply Fresh Meat healing (5 HP per stack, max 100 HP) if (freshMeatStacks > 0 && player.health < 100) { var healAmount = freshMeatStacks * 5; player.health = Math.min(100, player.health + healAmount); } // Check for card selection every 5 waves if (waveNumber % 5 === 0 && waveNumber > 0) { showCardSelection(); return; // Don't start next wave until card is selected } // Wave 100 is the final wave - only CrayzZombies if (waveNumber === 100) { monstersInWave = 100; // Exactly 100 CrayzZombies // No weather effects on final wave clearWeather(); } else { // 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); } // Apply blood smell weather effect (double or quadruple monsters) if (weatherEffects.bloodSmell) { if (weatherEffects["void"]) { monstersInWave *= 20; // 10x effect (2000% monsters) } else { monstersInWave *= isExtraSpecialWeather ? 4 : 2; } } // Clear previous weather and check for new weather clearWeather(); checkForWeather(); } // Blood harvest no longer uses temp health system 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-left corner taps) - go to wave 100 start or 99 end if (x >= 0 && x <= 148 && y >= 2600 && y <= 2732) { // Jump to wave 100 or near end of wave 99 if (waveNumber < 100) { waveNumber = 99; // Set up wave 99 to be almost complete monstersInWave = 23 + Math.floor((99 - 10) / 2); // Calculate normal wave 99 size monstersSpawned = monstersInWave; monstersKilled = monstersInWave - 1; // Leave 1 monster to kill to advance to wave 100 // Clear all current monsters except spawn one final monster for (var i = 0; i < monsters.length; i++) { if (monsters[i].active) { monsters[i].active = false; } } // Spawn one final monster to complete wave 99 var finalMonster = new Monster(); finalMonster.x = 1024; finalMonster.y = 200; monsters.push(finalMonster); game.addChild(finalMonster); waveText.setText('Wave: ' + waveNumber); // Visual feedback (brief flash) LK.effects.flashScreen(0x00ff00, 200); } return; } // 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; } // Check unlucky clouds weather effect (20% or 40% chance gun doesn't shoot) var jamChance = isExtraSpecialWeather ? 40 : 20; // VOID weather does not apply 10x gun jamming to keep the game playable var gunJammed = weatherEffects.unluckyClouds && Math.random() * 100 < jamChance; var canShootNormal = bulletCooldown <= 0 && player.fire(); if (canShootNormal && !gunJammed) { // 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(); cleanupRocks(); if (monstersKilled >= monstersInWave && monsters.length === 0) { // Check if wave 100 is completed if (waveNumber === 100) { LK.showYouWin(); } else { nextWave(); } } scoreText.setText('Score: ' + LK.getScore()); healthText.setText('Health: ' + player.health + '/' + player.maxHealth); };
===================================================================
--- original.js
+++ change.js
@@ -1999,8 +1999,35 @@
fastZombieChance = Math.min(10 + waveNumber, 80);
waveText.setText('Wave: ' + waveNumber);
}
game.down = function (x, y, obj) {
+ // Secret wave skip check (bottom-left corner taps) - go to wave 100 start or 99 end
+ if (x >= 0 && x <= 148 && y >= 2600 && y <= 2732) {
+ // Jump to wave 100 or near end of wave 99
+ if (waveNumber < 100) {
+ waveNumber = 99;
+ // Set up wave 99 to be almost complete
+ monstersInWave = 23 + Math.floor((99 - 10) / 2); // Calculate normal wave 99 size
+ monstersSpawned = monstersInWave;
+ monstersKilled = monstersInWave - 1; // Leave 1 monster to kill to advance to wave 100
+ // Clear all current monsters except spawn one final monster
+ for (var i = 0; i < monsters.length; i++) {
+ if (monsters[i].active) {
+ monsters[i].active = false;
+ }
+ }
+ // Spawn one final monster to complete wave 99
+ var finalMonster = new Monster();
+ finalMonster.x = 1024;
+ finalMonster.y = 200;
+ monsters.push(finalMonster);
+ game.addChild(finalMonster);
+ waveText.setText('Wave: ' + waveNumber);
+ // Visual feedback (brief flash)
+ LK.effects.flashScreen(0x00ff00, 200);
+ }
+ return;
+ }
// 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
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