User prompt
please adjust player reaches maximum movement speed upgrade on weapon upgrade level 25
User prompt
please adjust add two creeps extra for every wave after wave 1
User prompt
ok slightly increase threshold of creep proximity before player takes damage
User prompt
please fix the problem when the player moves the creeps hit the player from too far away before stationary threshold of player damage
User prompt
reduce the distance of all creeps before they can damage and hit the player
User prompt
please adjust all the creeps to be closer to the player before they hit the player
User prompt
please add effect once player hit points reach zero the player explodes in flames dead ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
please increase the frequency of shield dropped by creeps to 30%, please add effect to player if hit by creeps the player is bumped away slightly and flickers red slightly for one second, while flickering red the player is immune to all damage ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
please reduce the frequency of shield upgrades dropped by creeps
User prompt
add effect on bullets they rotate and emanate electric power ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
please adjust weapon upgrade lvl 50 to shoot 6 shots in burst fire pattern an Hexa Shot
User prompt
please adjust weapon upgrade level 40 to shoot 5 shots two double shots in burst fire with one shot centered in the middle of the shots
User prompt
please adjust the weapon upgrade lvl 30 to shoot 4 shots two double shots in burst fire
User prompt
Reset weapon upgrade abilities for each level, weapon upgrade lvl 10 shoots a double shot , weapon upgrade lvl 20 shoots 3 shots in parallels of each other, weapon upgrade lvl 30 shoots two double shots in parallels
User prompt
Please adjust player weapon fire rate, on weapon upgrade lvl10 the player shoots double shot , lvl20 the player shoots triple shot (the bullets are parallel to each other close proximity) on level30 the player shoots 4 bullets the 4 bullets spiral in an rotating faction in straight line, on level 40 the player shoots two double shots in burst fire parallel to each other in an straight line on lvl 50 the player shoots 6 bullets in burst fire in an spiral rotating pattern in a straight line ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
please adjust weapon upgrade on level 10 it shoots double shot and level 20 it shoots triple shot, on level 30 it shoots two double shots and level 40 it shoots two triple shots
User prompt
Slightly Increase player size
User prompt
please adjust the top UI to show the weapon type but not the wording "Weapon type:" only the current weapon type the player is using
User prompt
please add Weapon Type: to the above UI beneath Weapon level in the top right. The player starts the game with weapon type: Blaster
User prompt
Please change the weapon counter on top right and rename it Weapon Level
User prompt
please adjust the drop of health potions, only Boss creeps drop health potions on each 10'th Wave , the Boss creep drops an health potion and it heals the player for 50 hit points, the Boss creep also drops an Armor upgrade that increases the players armor to 50 points, Armor always takes damage before the health points takes damage and shields always takes damage points before Armor or health points
User prompt
please remove the Score counter in top right and add and Weapon level counter in top right
User prompt
please adjust the shield upgrade to last 60 seconds each and the time of the shield can add up if another shield is collected while an shield is active but the duration of the shield cant exceed 180 seconds in total
User prompt
please adjust the period of the shield upgrade to 60 seconds not 30 seconds
User prompt
please adjust the shield counter to be under the armor counter
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BossCreep = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bossCreep', { anchorX: 0.5, anchorY: 0.5 }); self.health = 12000; // Base health for first boss (30x increase) self.maxHealth = 12000; self.speed = 0.8; // Slower than elite creeps self.damage = 50; // More damage than elite creeps self.targetX = 0; self.targetY = 0; self.hasReachedPlayer = false; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { return true; // Return true if boss is dead } return false; }; self.update = function () { // Reset flag to allow continued movement after hitting player self.hasReachedPlayer = false; // Continuously update target to player's current position self.targetX = player.x; self.targetY = player.y; var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Calculate avoidance force from other creeps var avoidanceX = 0; var avoidanceY = 0; var avoidanceRadius = 180; // Largest avoidance radius for boss creeps for (var i = 0; i < creeps.length; i++) { var otherCreep = creeps[i]; if (otherCreep !== self) { var otherDx = self.x - otherCreep.x; var otherDy = self.y - otherCreep.y; var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDistance < avoidanceRadius && otherDistance > 0) { var avoidanceStrength = (avoidanceRadius - otherDistance) / avoidanceRadius; avoidanceX += otherDx / otherDistance * avoidanceStrength; avoidanceY += otherDy / otherDistance * avoidanceStrength; } } } // Combine movement towards player with avoidance var moveX = dx / distance * self.speed + avoidanceX * 0.3; var moveY = dy / distance * self.speed + avoidanceY * 0.3; self.x += moveX; self.y += moveY; } else { self.hasReachedPlayer = true; } }; 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 = 8; self.velocityX = 0; self.velocityY = 0; self.damage = 20; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var Creep = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('creep', { anchorX: 0.5, anchorY: 0.5 }); self.health = 60; // Base health requiring 3 hits (20 damage per hit) self.maxHealth = 60; self.speed = 2; self.damage = 15; self.targetX = 0; self.targetY = 0; self.hasReachedPlayer = false; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { return true; // Return true if creep is dead } return false; }; self.update = function () { // Reset flag to allow continued movement after hitting player self.hasReachedPlayer = false; // Continuously update target to player's current position self.targetX = player.x; self.targetY = player.y; var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Calculate avoidance force from other creeps var avoidanceX = 0; var avoidanceY = 0; var avoidanceRadius = 80; // Distance to avoid other creeps for (var i = 0; i < creeps.length; i++) { var otherCreep = creeps[i]; if (otherCreep !== self) { var otherDx = self.x - otherCreep.x; var otherDy = self.y - otherCreep.y; var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDistance < avoidanceRadius && otherDistance > 0) { var avoidanceStrength = (avoidanceRadius - otherDistance) / avoidanceRadius; avoidanceX += otherDx / otherDistance * avoidanceStrength; avoidanceY += otherDy / otherDistance * avoidanceStrength; } } } // Combine movement towards player with avoidance var moveX = dx / distance * self.speed + avoidanceX * 0.5; var moveY = dy / distance * self.speed + avoidanceY * 0.5; self.x += moveX; self.y += moveY; } else { self.hasReachedPlayer = true; } }; return self; }); var EliteCreep = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('eliteCreep', { anchorX: 0.5, anchorY: 0.5 }); self.health = 150; // Much higher base health self.maxHealth = 150; self.speed = 1.2; // Slower than regular creeps self.damage = 35; // More damage than regular creeps self.targetX = 0; self.targetY = 0; self.hasReachedPlayer = false; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { return true; // Return true if creep is dead } return false; }; self.update = function () { // Reset flag to allow continued movement after hitting player self.hasReachedPlayer = false; // Continuously update target to player's current position self.targetX = player.x; self.targetY = player.y; var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Calculate avoidance force from other creeps var avoidanceX = 0; var avoidanceY = 0; var avoidanceRadius = 120; // Larger avoidance radius for elite creeps for (var i = 0; i < creeps.length; i++) { var otherCreep = creeps[i]; if (otherCreep !== self) { var otherDx = self.x - otherCreep.x; var otherDy = self.y - otherCreep.y; var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDistance < avoidanceRadius && otherDistance > 0) { var avoidanceStrength = (avoidanceRadius - otherDistance) / avoidanceRadius; avoidanceX += otherDx / otherDistance * avoidanceStrength; avoidanceY += otherDy / otherDistance * avoidanceStrength; } } } // Combine movement towards player with avoidance var moveX = dx / distance * self.speed + avoidanceX * 0.5; var moveY = dy / distance * self.speed + avoidanceY * 0.5; self.x += moveX; self.y += moveY; } else { self.hasReachedPlayer = true; } }; return self; }); var Pickup = Container.expand(function (type) { var self = Container.call(this); self.type = type; var assetName; if (type === 'health') { assetName = 'healthPotion'; } else if (type === 'damage') { assetName = 'damageUpgrade'; } else if (type === 'speed') { assetName = 'damageUpgrade'; // Reuse red upgrade for speed } else if (type === 'attackSpeed') { assetName = 'damageUpgrade'; // Reuse red upgrade for attack speed } else if (type === 'shield') { assetName = 'shieldUpgrade'; } var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 1800; // 30 seconds at 60fps self.update = function () { self.lifetime--; if (self.lifetime < 300) { // Start fading at 5 seconds remaining graphics.alpha = self.lifetime / 300; } // Magnetic pull effect when player is close if (player) { var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Start pulling when within 150 pixels if (distance < 150 && distance > 10) { var pullSpeed = 8; // Fast pull speed var normalizedX = dx / distance; var normalizedY = dy / distance; self.x += normalizedX * pullSpeed; self.y += normalizedY * pullSpeed; } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 100; self.health = 100; self.defense = 0; self.baseDamage = 20; // Base damage that never changes self.damage = 20; // Current damage (base + upgrades) self.weaponUpgrades = 0; // Track weapon upgrades for double shot self.lastShootTime = 0; self.shootCooldown = 40; // Fixed cooldown that never changes self.baseSpeed = 4; // Base movement speed self.moveSpeed = 4; // Current movement speed (base + upgrades) self.shieldActive = false; self.shieldHealth = 0; self.shieldMaxHealth = 80; self.shieldDuration = 0; // In game ticks (60 fps) self.shieldAura = null; // Movement direction tracking self.lastX = 0; self.lastY = 0; self.currentDirection = 0; // 0 = north (default) self.takeDamage = function (amount) { if (self.shieldActive && self.shieldHealth > 0) { // Shield absorbs damage self.shieldHealth -= amount; if (self.shieldHealth <= 0) { // Shield broken self.shieldHealth = 0; self.shieldActive = false; self.shieldDuration = 0; if (self.shieldAura) { self.shieldAura.destroy(); self.shieldAura = null; } } // Flash blue when shield takes damage tween(graphics, { tint: 0x4444FF }, { duration: 100, onFinish: function onFinish() { tween(graphics, { tint: 0xFFFFFF }, { duration: 100 }); } }); } else { // Normal damage to health var actualDamage = Math.max(1, amount - self.defense); self.health -= actualDamage; if (self.health < 0) self.health = 0; // Flash red when taking damage tween(graphics, { tint: 0xFF0000 }, { duration: 100, onFinish: function onFinish() { tween(graphics, { tint: 0xFFFFFF }, { duration: 100 }); } }); } LK.getSound('hit').play(); }; self.heal = function (amount) { self.health = Math.min(self.maxHealth, self.health + amount); }; self.addDamage = function (amount) { self.damage += amount; self.weaponUpgrades++; // Track weapon upgrades }; self.addSpeed = function (amount) { self.moveSpeed += amount; }; self.activateShield = function () { if (!self.shieldActive) { // First time activation self.shieldActive = true; self.shieldHealth = 40; // Start with 40 hit points self.shieldDuration = 1800; // 30 seconds at 60fps // Create shield visual self.shieldAura = self.addChild(LK.getAsset('shieldAura', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6, tint: 0x6666FF })); } else { // Shield already active, add duration up to max 180 seconds self.shieldDuration = Math.min(10800, self.shieldDuration + 1800); // Max 180 seconds // Add shield health up to max 80 hit points self.shieldHealth = Math.min(80, self.shieldHealth + 40); // Ensure shield visual is displayed if (!self.shieldAura) { self.shieldAura = self.addChild(LK.getAsset('shieldAura', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6, tint: 0x6666FF })); } } }; self.canShoot = function () { return LK.ticks - self.lastShootTime > self.shootCooldown; }; self.shoot = function (targetX, targetY) { if (!self.canShoot()) return null; self.lastShootTime = LK.ticks; var bullets = []; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var normalizedX = dx / distance; var normalizedY = dy / distance; var angle = Math.atan2(normalizedY, normalizedX); if (self.weaponUpgrades >= 30) { // Quad shot for 30+ upgrades var spreadAngle = 0.25; // Center bullet var bullet1 = new Bullet(); bullet1.x = self.x; bullet1.y = self.y; bullet1.velocityX = normalizedX * bullet1.speed; bullet1.velocityY = normalizedY * bullet1.speed; bullet1.damage = self.damage; bullets.push(bullet1); // Left bullet var bullet2 = new Bullet(); bullet2.x = self.x; bullet2.y = self.y; bullet2.velocityX = Math.cos(angle - spreadAngle) * bullet2.speed; bullet2.velocityY = Math.sin(angle - spreadAngle) * bullet2.speed; bullet2.damage = self.damage; bullets.push(bullet2); // Right bullet var bullet3 = new Bullet(); bullet3.x = self.x; bullet3.y = self.y; bullet3.velocityX = Math.cos(angle + spreadAngle) * bullet3.speed; bullet3.velocityY = Math.sin(angle + spreadAngle) * bullet3.speed; bullet3.damage = self.damage; bullets.push(bullet3); // Far left bullet var bullet4 = new Bullet(); bullet4.x = self.x; bullet4.y = self.y; bullet4.velocityX = Math.cos(angle - spreadAngle * 2) * bullet4.speed; bullet4.velocityY = Math.sin(angle - spreadAngle * 2) * bullet4.speed; bullet4.damage = self.damage; bullets.push(bullet4); } else if (self.weaponUpgrades >= 20) { // Triple shot V pattern at 20+ upgrades var spreadAngle = 0.3; // Wider spread for V pattern // Center bullet var bullet1 = new Bullet(); bullet1.x = self.x; bullet1.y = self.y; bullet1.velocityX = normalizedX * bullet1.speed; bullet1.velocityY = normalizedY * bullet1.speed; bullet1.damage = self.damage; bullets.push(bullet1); // Left bullet var bullet2 = new Bullet(); bullet2.x = self.x; bullet2.y = self.y; bullet2.velocityX = Math.cos(angle - spreadAngle) * bullet2.speed; bullet2.velocityY = Math.sin(angle - spreadAngle) * bullet2.speed; bullet2.damage = self.damage; bullets.push(bullet2); // Right bullet var bullet3 = new Bullet(); bullet3.x = self.x; bullet3.y = self.y; bullet3.velocityX = Math.cos(angle + spreadAngle) * bullet3.speed; bullet3.velocityY = Math.sin(angle + spreadAngle) * bullet3.speed; bullet3.damage = self.damage; bullets.push(bullet3); } else if (self.weaponUpgrades >= 10) { // Double shot side by side at 10+ upgrades var offsetDistance = 20; // Distance between bullets var perpX = -normalizedY; // Perpendicular to direction var perpY = normalizedX; // First bullet (left side) var bullet1 = new Bullet(); bullet1.x = self.x + perpX * offsetDistance; bullet1.y = self.y + perpY * offsetDistance; bullet1.velocityX = normalizedX * bullet1.speed; bullet1.velocityY = normalizedY * bullet1.speed; bullet1.damage = self.damage; bullets.push(bullet1); // Second bullet (right side) var bullet2 = new Bullet(); bullet2.x = self.x - perpX * offsetDistance; bullet2.y = self.y - perpY * offsetDistance; bullet2.velocityX = normalizedX * bullet2.speed; bullet2.velocityY = normalizedY * bullet2.speed; bullet2.damage = self.damage; bullets.push(bullet2); } else { // Single shot for upgrades < 10 var bullet1 = new Bullet(); bullet1.x = self.x; bullet1.y = self.y; bullet1.velocityX = normalizedX * bullet1.speed; bullet1.velocityY = normalizedY * bullet1.speed; bullet1.damage = self.damage; bullets.push(bullet1); } LK.getSound('shoot').play(); return bullets; }; self.update = function () { // Track movement direction and animate rotation if (self.lastX !== undefined && self.lastY !== undefined) { var deltaX = self.x - self.lastX; var deltaY = self.y - self.lastY; var moveDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Only update direction if player is actually moving if (moveDistance > 0.5) { var targetDirection = Math.atan2(deltaY, deltaX); // Convert to 0-360 degree system where 0 is north targetDirection = targetDirection + Math.PI / 2; if (targetDirection < 0) targetDirection += Math.PI * 2; if (targetDirection > Math.PI * 2) targetDirection -= Math.PI * 2; // Smoothly rotate to face movement direction var currentRotation = graphics.rotation; var angleDiff = targetDirection - currentRotation; // Handle wrap around for shortest rotation path if (angleDiff > Math.PI) angleDiff -= Math.PI * 2; if (angleDiff < -Math.PI) angleDiff += Math.PI * 2; // Apply smooth rotation var rotationSpeed = 0.15; graphics.rotation += angleDiff * rotationSpeed; } } // Update last position for next frame self.lastX = self.x; self.lastY = self.y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game(); /**** * Game Code ****/ // Add background image var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); var player = null; var creeps = []; var bullets = []; var pickups = []; var currentWave = 1; var creepsInWave = 5; var creepsSpawned = 0; var eliteCreepsInWave = 1; // Number of elite creeps equals wave number var eliteCreepsSpawned = 0; var waveCompleted = false; var nextWaveTimer = 0; var spawnTimer = 0; // Score tracking variables var gameScore = 0; // Boss system variables var currentBoss = null; var bossWave = false; // UI Elements var healthText = new Text2('Health: 100', { size: 40, fill: 0xFFFFFF }); healthText.anchor.set(0, 0); LK.gui.topLeft.addChild(healthText); healthText.x = 120; // Offset from left edge var armorText = new Text2('Armor: 0', { size: 40, fill: 0xFFFFFF }); armorText.anchor.set(0, 0); LK.gui.topLeft.addChild(armorText); armorText.x = 120; // Offset from left edge armorText.y = 45; // Below health text var waveText = new Text2('Wave: 1', { size: 40, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); LK.gui.topRight.addChild(scoreText); // Boss health bar (initially hidden) var bossHealthBar = new Text2('', { size: 35, fill: 0xFF4444 }); bossHealthBar.anchor.set(0.5, 0); LK.gui.top.addChild(bossHealthBar); bossHealthBar.y = 50; bossHealthBar.alpha = 0; // Hidden initially // Shield status text var shieldText = new Text2('', { size: 35, fill: 0x4444FF }); shieldText.anchor.set(0, 0); LK.gui.topLeft.addChild(shieldText); shieldText.x = 120; // Offset from left edge shieldText.y = 90; // Below armor text // Initialize player player = game.addChild(new Player()); player.x = 1024; player.y = 1366; function spawnCreep() { var creep = new Creep(); // Spawn from random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top creep.x = Math.random() * 2048; creep.y = -50; break; case 1: // Right creep.x = 2098; creep.y = Math.random() * 2732; break; case 2: // Bottom creep.x = Math.random() * 2048; creep.y = 2782; break; case 3: // Left creep.x = -50; creep.y = Math.random() * 2732; break; } creep.targetX = player.x; creep.targetY = player.y; creep.health += (currentWave - 1) * 20; // Scale health with wave (20 per wave) creep.maxHealth = creep.health; creeps.push(creep); game.addChild(creep); } function spawnEliteCreep() { var eliteCreep = new EliteCreep(); // Spawn from random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top eliteCreep.x = Math.random() * 2048; eliteCreep.y = -50; break; case 1: // Right eliteCreep.x = 2098; eliteCreep.y = Math.random() * 2732; break; case 2: // Bottom eliteCreep.x = Math.random() * 2048; eliteCreep.y = 2782; break; case 3: // Left eliteCreep.x = -50; eliteCreep.y = Math.random() * 2732; break; } eliteCreep.targetX = player.x; eliteCreep.targetY = player.y; eliteCreep.health += (currentWave - 1) * 40; // Scale health with wave (40 per wave for elites) eliteCreep.maxHealth = eliteCreep.health; creeps.push(eliteCreep); game.addChild(eliteCreep); } function spawnBoss() { var boss = new BossCreep(); // Spawn from random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top boss.x = Math.random() * 2048; boss.y = -75; break; case 1: // Right boss.x = 2123; boss.y = Math.random() * 2732; break; case 2: // Bottom boss.x = Math.random() * 2048; boss.y = 2807; break; case 3: // Left boss.x = -75; boss.y = Math.random() * 2732; break; } boss.targetX = player.x; boss.targetY = player.y; // Calculate boss health based on wave (doubles each boss wave) var bossNumber = Math.floor(currentWave / 10); boss.health = 12000 * Math.pow(2, bossNumber - 1); boss.maxHealth = boss.health; currentBoss = boss; creeps.push(boss); game.addChild(boss); // Show boss health bar bossHealthBar.alpha = 1; tween(bossHealthBar, { scaleX: 1.1, scaleY: 1.1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(bossHealthBar, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } function dropPickup(x, y) { if (Math.random() < 0.3) { // 30% chance to drop something var pickupType; var rand = Math.random(); if (rand < 0.2) { // Health upgrade frequency increased to 20% pickupType = 'health'; } else if (rand < 0.47) { // Damage upgrade frequency reduced to 27% pickupType = 'damage'; } else if (rand < 0.89) { // Shield upgrade increased to 42% pickupType = 'shield'; } else { // Speed upgrade remains at 11% pickupType = 'speed'; } var pickup = new Pickup(pickupType); pickup.x = x; pickup.y = y; pickups.push(pickup); game.addChild(pickup); } } function startNextWave() { currentWave++; // Check if this is a boss wave (every 10th wave) if (currentWave % 10 === 0) { bossWave = true; creepsInWave = 0; // No regular creeps on boss waves eliteCreepsInWave = 0; // No elite creeps on boss waves } else { bossWave = false; creepsInWave = 8 + Math.floor(currentWave * 2.5); // More creeps per wave eliteCreepsInWave = currentWave; // Number of elite creeps equals wave number } creepsSpawned = 0; eliteCreepsSpawned = 0; waveCompleted = false; waveText.setText('Wave: ' + currentWave + (bossWave ? ' (BOSS)' : '')); } // Player movement variables var targetX = 1024; var targetY = 1366; var isMoving = false; game.down = function (x, y, obj) { targetX = x; targetY = y; isMoving = true; }; game.move = function (x, y, obj) { if (isMoving) { targetX = x; targetY = y; } }; game.up = function (x, y, obj) { isMoving = false; }; game.update = function () { // Process pickups FIRST to ensure upgrades always activate before any damage for (var i = pickups.length - 1; i >= 0; i--) { var pickup = pickups[i]; // Check pickup collection using both intersection and distance var dx = pickup.x - player.x; var dy = pickup.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); var collected = pickup.intersects(player) || distance < 50; if (collected) { // Immediately activate upgrade effect and calculate points var pickupPoints = 150; // Default points switch (pickup.type) { case 'health': // Check if player is at full health before healing var wasAtFullHealth = player.health >= player.maxHealth; player.heal(25); // Double points if player was at full health when picking up health potion if (wasAtFullHealth) { pickupPoints = 300; } break; case 'damage': player.addDamage(5); break; case 'speed': player.addSpeed(1); break; case 'shield': player.activateShield(); break; } // Give points for collecting upgrades gameScore += pickupPoints; LK.setScore(LK.getScore() + pickupPoints); LK.getSound('pickup').play(); pickup.destroy(); pickups.splice(i, 1); } else if (pickup.lifetime <= 0) { pickup.destroy(); pickups.splice(i, 1); } } // Player movement if (isMoving) { var dx = targetX - player.x; var dy = targetY - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { player.x += dx / distance * player.moveSpeed; player.y += dy / distance * player.moveSpeed; } } // Automatic shooting at closest creep if (creeps.length > 0 && player.canShoot()) { var closestCreep = null; var closestDistance = Infinity; for (var i = 0; i < creeps.length; i++) { var creep = creeps[i]; var dx = creep.x - player.x; var dy = creep.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestCreep = creep; } } if (closestCreep) { var shotBullets = player.shoot(closestCreep.x, closestCreep.y); if (shotBullets) { for (var k = 0; k < shotBullets.length; k++) { bullets.push(shotBullets[k]); game.addChild(shotBullets[k]); } } } } // Update shield system if (player.shieldActive) { player.shieldDuration--; if (player.shieldDuration <= 0 || player.shieldHealth <= 0) { player.shieldActive = false; player.shieldHealth = 0; player.shieldDuration = 0; if (player.shieldAura) { player.shieldAura.destroy(); player.shieldAura = null; } } else if (player.shieldAura) { // Animate shield aura player.shieldAura.rotation += 0.05; // Flash when shield is about to expire (last 5 seconds) if (player.shieldDuration < 300) { player.shieldAura.alpha = 0.3 + 0.3 * Math.sin(LK.ticks * 0.3); } } } // Player defense is based on shield player.defense = 0; // Update player health display healthText.setText('Health: ' + player.health); armorText.setText('Armor: ' + player.defense); scoreText.setText('Score: ' + gameScore); // Update shield display if (player.shieldActive) { var shieldTimeLeft = Math.ceil(player.shieldDuration / 60); shieldText.setText('Shield: ' + player.shieldHealth + '/' + player.shieldMaxHealth + ' (' + shieldTimeLeft + 's)'); } else { shieldText.setText(''); } // Update boss health bar if (currentBoss && bossHealthBar.alpha > 0) { var healthPercent = Math.round(currentBoss.health / currentBoss.maxHealth * 100); bossHealthBar.setText('BOSS: ' + currentBoss.health + '/' + currentBoss.maxHealth + ' (' + healthPercent + '%)'); } // Check game over if (player.health <= 0) { LK.showGameOver(); return; } // Wave management if (!waveCompleted) { if (bossWave) { // Boss wave logic if (!currentBoss && creeps.length === 0) { spawnBoss(); } // Check if boss wave is complete if (currentBoss && creeps.length === 0) { waveCompleted = true; nextWaveTimer = 180; // 3 second break currentBoss = null; // Hide boss health bar tween(bossHealthBar, { alpha: 0 }, { duration: 500 }); } } else { // Regular wave logic // Spawn creeps if (creepsSpawned < creepsInWave || eliteCreepsSpawned < eliteCreepsInWave) { spawnTimer++; if (spawnTimer >= 60) { // Spawn every second if (creepsSpawned < creepsInWave) { spawnCreep(); creepsSpawned++; } else if (eliteCreepsSpawned < eliteCreepsInWave) { spawnEliteCreep(); eliteCreepsSpawned++; } spawnTimer = 0; } } // Check if wave is complete if (creepsSpawned >= creepsInWave && eliteCreepsSpawned >= eliteCreepsInWave && creeps.length === 0) { waveCompleted = true; nextWaveTimer = 180; // 3 second break } } } else { nextWaveTimer--; if (nextWaveTimer <= 0) { startNextWave(); } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; // Check if bullet is off screen if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) { bullet.destroy(); bullets.splice(i, 1); continue; } // Check bullet-creep collisions var hitCreep = false; for (var j = creeps.length - 1; j >= 0; j--) { var creep = creeps[j]; if (bullet.intersects(creep)) { var isDead = creep.takeDamage(bullet.damage); if (isDead) { // Give points for killing creeps var scoreValue; if (creep instanceof BossCreep) { scoreValue = 1000; // High score for boss // Boss always drops multiple pickups for (var p = 0; p < 3; p++) { dropPickup(creep.x + (Math.random() - 0.5) * 100, creep.y + (Math.random() - 0.5) * 100); } } else if (creep instanceof EliteCreep) { scoreValue = 200; // Elite creeps have 50% chance to drop weapon damage upgrade if (Math.random() < 0.5) { var damagePickup = new Pickup('damage'); damagePickup.x = creep.x; damagePickup.y = creep.y; pickups.push(damagePickup); game.addChild(damagePickup); } } else { scoreValue = 100; } gameScore += scoreValue; LK.setScore(LK.getScore() + scoreValue); if (!(creep instanceof BossCreep)) { dropPickup(creep.x, creep.y); } creep.destroy(); creeps.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); hitCreep = true; break; } } } // Update creeps for (var i = creeps.length - 1; i >= 0; i--) { var creep = creeps[i]; // Check if creep reached player - adjust hit detection based on shield status var canHitPlayer = false; if (player.shieldActive) { // When shield is active, creeps must be much closer to hit var dx = creep.x - player.x; var dy = creep.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); canHitPlayer = distance < 30; // Reduced hit distance when shield is active } else { // Normal hit detection when no shield canHitPlayer = creep.hasReachedPlayer || creep.intersects(player); } if (canHitPlayer) { if (!creep.hasReachedPlayer) { player.takeDamage(creep.damage); creep.hasReachedPlayer = true; // Calculate bump direction (away from creep) var bumpDx = player.x - creep.x; var bumpDy = player.y - creep.y; var bumpDistance = Math.sqrt(bumpDx * bumpDx + bumpDy * bumpDy); if (bumpDistance > 0) { var normalizedBumpX = bumpDx / bumpDistance; var normalizedBumpY = bumpDy / bumpDistance; var bumpStrength = 30; // How far to bump the player // Bump the player away from the creep tween(player, { x: player.x + normalizedBumpX * bumpStrength, y: player.y + normalizedBumpY * bumpStrength }, { duration: 200, easing: tween.easeOut }); } } // Don't destroy creep - it stays alive after hitting player } } // Pickup processing moved to beginning of update loop };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BossCreep = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bossCreep', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 12000; // Base health for first boss (30x increase)
self.maxHealth = 12000;
self.speed = 0.8; // Slower than elite creeps
self.damage = 50; // More damage than elite creeps
self.targetX = 0;
self.targetY = 0;
self.hasReachedPlayer = false;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
return true; // Return true if boss is dead
}
return false;
};
self.update = function () {
// Reset flag to allow continued movement after hitting player
self.hasReachedPlayer = false;
// Continuously update target to player's current position
self.targetX = player.x;
self.targetY = player.y;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Calculate avoidance force from other creeps
var avoidanceX = 0;
var avoidanceY = 0;
var avoidanceRadius = 180; // Largest avoidance radius for boss creeps
for (var i = 0; i < creeps.length; i++) {
var otherCreep = creeps[i];
if (otherCreep !== self) {
var otherDx = self.x - otherCreep.x;
var otherDy = self.y - otherCreep.y;
var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy);
if (otherDistance < avoidanceRadius && otherDistance > 0) {
var avoidanceStrength = (avoidanceRadius - otherDistance) / avoidanceRadius;
avoidanceX += otherDx / otherDistance * avoidanceStrength;
avoidanceY += otherDy / otherDistance * avoidanceStrength;
}
}
}
// Combine movement towards player with avoidance
var moveX = dx / distance * self.speed + avoidanceX * 0.3;
var moveY = dy / distance * self.speed + avoidanceY * 0.3;
self.x += moveX;
self.y += moveY;
} else {
self.hasReachedPlayer = true;
}
};
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 = 8;
self.velocityX = 0;
self.velocityY = 0;
self.damage = 20;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var Creep = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('creep', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 60; // Base health requiring 3 hits (20 damage per hit)
self.maxHealth = 60;
self.speed = 2;
self.damage = 15;
self.targetX = 0;
self.targetY = 0;
self.hasReachedPlayer = false;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
return true; // Return true if creep is dead
}
return false;
};
self.update = function () {
// Reset flag to allow continued movement after hitting player
self.hasReachedPlayer = false;
// Continuously update target to player's current position
self.targetX = player.x;
self.targetY = player.y;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Calculate avoidance force from other creeps
var avoidanceX = 0;
var avoidanceY = 0;
var avoidanceRadius = 80; // Distance to avoid other creeps
for (var i = 0; i < creeps.length; i++) {
var otherCreep = creeps[i];
if (otherCreep !== self) {
var otherDx = self.x - otherCreep.x;
var otherDy = self.y - otherCreep.y;
var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy);
if (otherDistance < avoidanceRadius && otherDistance > 0) {
var avoidanceStrength = (avoidanceRadius - otherDistance) / avoidanceRadius;
avoidanceX += otherDx / otherDistance * avoidanceStrength;
avoidanceY += otherDy / otherDistance * avoidanceStrength;
}
}
}
// Combine movement towards player with avoidance
var moveX = dx / distance * self.speed + avoidanceX * 0.5;
var moveY = dy / distance * self.speed + avoidanceY * 0.5;
self.x += moveX;
self.y += moveY;
} else {
self.hasReachedPlayer = true;
}
};
return self;
});
var EliteCreep = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('eliteCreep', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 150; // Much higher base health
self.maxHealth = 150;
self.speed = 1.2; // Slower than regular creeps
self.damage = 35; // More damage than regular creeps
self.targetX = 0;
self.targetY = 0;
self.hasReachedPlayer = false;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
return true; // Return true if creep is dead
}
return false;
};
self.update = function () {
// Reset flag to allow continued movement after hitting player
self.hasReachedPlayer = false;
// Continuously update target to player's current position
self.targetX = player.x;
self.targetY = player.y;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Calculate avoidance force from other creeps
var avoidanceX = 0;
var avoidanceY = 0;
var avoidanceRadius = 120; // Larger avoidance radius for elite creeps
for (var i = 0; i < creeps.length; i++) {
var otherCreep = creeps[i];
if (otherCreep !== self) {
var otherDx = self.x - otherCreep.x;
var otherDy = self.y - otherCreep.y;
var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy);
if (otherDistance < avoidanceRadius && otherDistance > 0) {
var avoidanceStrength = (avoidanceRadius - otherDistance) / avoidanceRadius;
avoidanceX += otherDx / otherDistance * avoidanceStrength;
avoidanceY += otherDy / otherDistance * avoidanceStrength;
}
}
}
// Combine movement towards player with avoidance
var moveX = dx / distance * self.speed + avoidanceX * 0.5;
var moveY = dy / distance * self.speed + avoidanceY * 0.5;
self.x += moveX;
self.y += moveY;
} else {
self.hasReachedPlayer = true;
}
};
return self;
});
var Pickup = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
var assetName;
if (type === 'health') {
assetName = 'healthPotion';
} else if (type === 'damage') {
assetName = 'damageUpgrade';
} else if (type === 'speed') {
assetName = 'damageUpgrade'; // Reuse red upgrade for speed
} else if (type === 'attackSpeed') {
assetName = 'damageUpgrade'; // Reuse red upgrade for attack speed
} else if (type === 'shield') {
assetName = 'shieldUpgrade';
}
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 1800; // 30 seconds at 60fps
self.update = function () {
self.lifetime--;
if (self.lifetime < 300) {
// Start fading at 5 seconds remaining
graphics.alpha = self.lifetime / 300;
}
// Magnetic pull effect when player is close
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Start pulling when within 150 pixels
if (distance < 150 && distance > 10) {
var pullSpeed = 8; // Fast pull speed
var normalizedX = dx / distance;
var normalizedY = dy / distance;
self.x += normalizedX * pullSpeed;
self.y += normalizedY * pullSpeed;
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 100;
self.health = 100;
self.defense = 0;
self.baseDamage = 20; // Base damage that never changes
self.damage = 20; // Current damage (base + upgrades)
self.weaponUpgrades = 0; // Track weapon upgrades for double shot
self.lastShootTime = 0;
self.shootCooldown = 40; // Fixed cooldown that never changes
self.baseSpeed = 4; // Base movement speed
self.moveSpeed = 4; // Current movement speed (base + upgrades)
self.shieldActive = false;
self.shieldHealth = 0;
self.shieldMaxHealth = 80;
self.shieldDuration = 0; // In game ticks (60 fps)
self.shieldAura = null;
// Movement direction tracking
self.lastX = 0;
self.lastY = 0;
self.currentDirection = 0; // 0 = north (default)
self.takeDamage = function (amount) {
if (self.shieldActive && self.shieldHealth > 0) {
// Shield absorbs damage
self.shieldHealth -= amount;
if (self.shieldHealth <= 0) {
// Shield broken
self.shieldHealth = 0;
self.shieldActive = false;
self.shieldDuration = 0;
if (self.shieldAura) {
self.shieldAura.destroy();
self.shieldAura = null;
}
}
// Flash blue when shield takes damage
tween(graphics, {
tint: 0x4444FF
}, {
duration: 100,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
} else {
// Normal damage to health
var actualDamage = Math.max(1, amount - self.defense);
self.health -= actualDamage;
if (self.health < 0) self.health = 0;
// Flash red when taking damage
tween(graphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
}
LK.getSound('hit').play();
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
};
self.addDamage = function (amount) {
self.damage += amount;
self.weaponUpgrades++; // Track weapon upgrades
};
self.addSpeed = function (amount) {
self.moveSpeed += amount;
};
self.activateShield = function () {
if (!self.shieldActive) {
// First time activation
self.shieldActive = true;
self.shieldHealth = 40; // Start with 40 hit points
self.shieldDuration = 1800; // 30 seconds at 60fps
// Create shield visual
self.shieldAura = self.addChild(LK.getAsset('shieldAura', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
tint: 0x6666FF
}));
} else {
// Shield already active, add duration up to max 180 seconds
self.shieldDuration = Math.min(10800, self.shieldDuration + 1800); // Max 180 seconds
// Add shield health up to max 80 hit points
self.shieldHealth = Math.min(80, self.shieldHealth + 40);
// Ensure shield visual is displayed
if (!self.shieldAura) {
self.shieldAura = self.addChild(LK.getAsset('shieldAura', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
tint: 0x6666FF
}));
}
}
};
self.canShoot = function () {
return LK.ticks - self.lastShootTime > self.shootCooldown;
};
self.shoot = function (targetX, targetY) {
if (!self.canShoot()) return null;
self.lastShootTime = LK.ticks;
var bullets = [];
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var normalizedX = dx / distance;
var normalizedY = dy / distance;
var angle = Math.atan2(normalizedY, normalizedX);
if (self.weaponUpgrades >= 30) {
// Quad shot for 30+ upgrades
var spreadAngle = 0.25;
// Center bullet
var bullet1 = new Bullet();
bullet1.x = self.x;
bullet1.y = self.y;
bullet1.velocityX = normalizedX * bullet1.speed;
bullet1.velocityY = normalizedY * bullet1.speed;
bullet1.damage = self.damage;
bullets.push(bullet1);
// Left bullet
var bullet2 = new Bullet();
bullet2.x = self.x;
bullet2.y = self.y;
bullet2.velocityX = Math.cos(angle - spreadAngle) * bullet2.speed;
bullet2.velocityY = Math.sin(angle - spreadAngle) * bullet2.speed;
bullet2.damage = self.damage;
bullets.push(bullet2);
// Right bullet
var bullet3 = new Bullet();
bullet3.x = self.x;
bullet3.y = self.y;
bullet3.velocityX = Math.cos(angle + spreadAngle) * bullet3.speed;
bullet3.velocityY = Math.sin(angle + spreadAngle) * bullet3.speed;
bullet3.damage = self.damage;
bullets.push(bullet3);
// Far left bullet
var bullet4 = new Bullet();
bullet4.x = self.x;
bullet4.y = self.y;
bullet4.velocityX = Math.cos(angle - spreadAngle * 2) * bullet4.speed;
bullet4.velocityY = Math.sin(angle - spreadAngle * 2) * bullet4.speed;
bullet4.damage = self.damage;
bullets.push(bullet4);
} else if (self.weaponUpgrades >= 20) {
// Triple shot V pattern at 20+ upgrades
var spreadAngle = 0.3; // Wider spread for V pattern
// Center bullet
var bullet1 = new Bullet();
bullet1.x = self.x;
bullet1.y = self.y;
bullet1.velocityX = normalizedX * bullet1.speed;
bullet1.velocityY = normalizedY * bullet1.speed;
bullet1.damage = self.damage;
bullets.push(bullet1);
// Left bullet
var bullet2 = new Bullet();
bullet2.x = self.x;
bullet2.y = self.y;
bullet2.velocityX = Math.cos(angle - spreadAngle) * bullet2.speed;
bullet2.velocityY = Math.sin(angle - spreadAngle) * bullet2.speed;
bullet2.damage = self.damage;
bullets.push(bullet2);
// Right bullet
var bullet3 = new Bullet();
bullet3.x = self.x;
bullet3.y = self.y;
bullet3.velocityX = Math.cos(angle + spreadAngle) * bullet3.speed;
bullet3.velocityY = Math.sin(angle + spreadAngle) * bullet3.speed;
bullet3.damage = self.damage;
bullets.push(bullet3);
} else if (self.weaponUpgrades >= 10) {
// Double shot side by side at 10+ upgrades
var offsetDistance = 20; // Distance between bullets
var perpX = -normalizedY; // Perpendicular to direction
var perpY = normalizedX;
// First bullet (left side)
var bullet1 = new Bullet();
bullet1.x = self.x + perpX * offsetDistance;
bullet1.y = self.y + perpY * offsetDistance;
bullet1.velocityX = normalizedX * bullet1.speed;
bullet1.velocityY = normalizedY * bullet1.speed;
bullet1.damage = self.damage;
bullets.push(bullet1);
// Second bullet (right side)
var bullet2 = new Bullet();
bullet2.x = self.x - perpX * offsetDistance;
bullet2.y = self.y - perpY * offsetDistance;
bullet2.velocityX = normalizedX * bullet2.speed;
bullet2.velocityY = normalizedY * bullet2.speed;
bullet2.damage = self.damage;
bullets.push(bullet2);
} else {
// Single shot for upgrades < 10
var bullet1 = new Bullet();
bullet1.x = self.x;
bullet1.y = self.y;
bullet1.velocityX = normalizedX * bullet1.speed;
bullet1.velocityY = normalizedY * bullet1.speed;
bullet1.damage = self.damage;
bullets.push(bullet1);
}
LK.getSound('shoot').play();
return bullets;
};
self.update = function () {
// Track movement direction and animate rotation
if (self.lastX !== undefined && self.lastY !== undefined) {
var deltaX = self.x - self.lastX;
var deltaY = self.y - self.lastY;
var moveDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Only update direction if player is actually moving
if (moveDistance > 0.5) {
var targetDirection = Math.atan2(deltaY, deltaX);
// Convert to 0-360 degree system where 0 is north
targetDirection = targetDirection + Math.PI / 2;
if (targetDirection < 0) targetDirection += Math.PI * 2;
if (targetDirection > Math.PI * 2) targetDirection -= Math.PI * 2;
// Smoothly rotate to face movement direction
var currentRotation = graphics.rotation;
var angleDiff = targetDirection - currentRotation;
// Handle wrap around for shortest rotation path
if (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
if (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
// Apply smooth rotation
var rotationSpeed = 0.15;
graphics.rotation += angleDiff * rotationSpeed;
}
}
// Update last position for next frame
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game();
/****
* Game Code
****/
// Add background image
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var player = null;
var creeps = [];
var bullets = [];
var pickups = [];
var currentWave = 1;
var creepsInWave = 5;
var creepsSpawned = 0;
var eliteCreepsInWave = 1; // Number of elite creeps equals wave number
var eliteCreepsSpawned = 0;
var waveCompleted = false;
var nextWaveTimer = 0;
var spawnTimer = 0;
// Score tracking variables
var gameScore = 0;
// Boss system variables
var currentBoss = null;
var bossWave = false;
// UI Elements
var healthText = new Text2('Health: 100', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthText);
healthText.x = 120; // Offset from left edge
var armorText = new Text2('Armor: 0', {
size: 40,
fill: 0xFFFFFF
});
armorText.anchor.set(0, 0);
LK.gui.topLeft.addChild(armorText);
armorText.x = 120; // Offset from left edge
armorText.y = 45; // Below health text
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var scoreText = new Text2('Score: 0', {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreText);
// Boss health bar (initially hidden)
var bossHealthBar = new Text2('', {
size: 35,
fill: 0xFF4444
});
bossHealthBar.anchor.set(0.5, 0);
LK.gui.top.addChild(bossHealthBar);
bossHealthBar.y = 50;
bossHealthBar.alpha = 0; // Hidden initially
// Shield status text
var shieldText = new Text2('', {
size: 35,
fill: 0x4444FF
});
shieldText.anchor.set(0, 0);
LK.gui.topLeft.addChild(shieldText);
shieldText.x = 120; // Offset from left edge
shieldText.y = 90; // Below armor text
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
function spawnCreep() {
var creep = new Creep();
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
creep.x = Math.random() * 2048;
creep.y = -50;
break;
case 1:
// Right
creep.x = 2098;
creep.y = Math.random() * 2732;
break;
case 2:
// Bottom
creep.x = Math.random() * 2048;
creep.y = 2782;
break;
case 3:
// Left
creep.x = -50;
creep.y = Math.random() * 2732;
break;
}
creep.targetX = player.x;
creep.targetY = player.y;
creep.health += (currentWave - 1) * 20; // Scale health with wave (20 per wave)
creep.maxHealth = creep.health;
creeps.push(creep);
game.addChild(creep);
}
function spawnEliteCreep() {
var eliteCreep = new EliteCreep();
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
eliteCreep.x = Math.random() * 2048;
eliteCreep.y = -50;
break;
case 1:
// Right
eliteCreep.x = 2098;
eliteCreep.y = Math.random() * 2732;
break;
case 2:
// Bottom
eliteCreep.x = Math.random() * 2048;
eliteCreep.y = 2782;
break;
case 3:
// Left
eliteCreep.x = -50;
eliteCreep.y = Math.random() * 2732;
break;
}
eliteCreep.targetX = player.x;
eliteCreep.targetY = player.y;
eliteCreep.health += (currentWave - 1) * 40; // Scale health with wave (40 per wave for elites)
eliteCreep.maxHealth = eliteCreep.health;
creeps.push(eliteCreep);
game.addChild(eliteCreep);
}
function spawnBoss() {
var boss = new BossCreep();
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
boss.x = Math.random() * 2048;
boss.y = -75;
break;
case 1:
// Right
boss.x = 2123;
boss.y = Math.random() * 2732;
break;
case 2:
// Bottom
boss.x = Math.random() * 2048;
boss.y = 2807;
break;
case 3:
// Left
boss.x = -75;
boss.y = Math.random() * 2732;
break;
}
boss.targetX = player.x;
boss.targetY = player.y;
// Calculate boss health based on wave (doubles each boss wave)
var bossNumber = Math.floor(currentWave / 10);
boss.health = 12000 * Math.pow(2, bossNumber - 1);
boss.maxHealth = boss.health;
currentBoss = boss;
creeps.push(boss);
game.addChild(boss);
// Show boss health bar
bossHealthBar.alpha = 1;
tween(bossHealthBar, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bossHealthBar, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
}
function dropPickup(x, y) {
if (Math.random() < 0.3) {
// 30% chance to drop something
var pickupType;
var rand = Math.random();
if (rand < 0.2) {
// Health upgrade frequency increased to 20%
pickupType = 'health';
} else if (rand < 0.47) {
// Damage upgrade frequency reduced to 27%
pickupType = 'damage';
} else if (rand < 0.89) {
// Shield upgrade increased to 42%
pickupType = 'shield';
} else {
// Speed upgrade remains at 11%
pickupType = 'speed';
}
var pickup = new Pickup(pickupType);
pickup.x = x;
pickup.y = y;
pickups.push(pickup);
game.addChild(pickup);
}
}
function startNextWave() {
currentWave++;
// Check if this is a boss wave (every 10th wave)
if (currentWave % 10 === 0) {
bossWave = true;
creepsInWave = 0; // No regular creeps on boss waves
eliteCreepsInWave = 0; // No elite creeps on boss waves
} else {
bossWave = false;
creepsInWave = 8 + Math.floor(currentWave * 2.5); // More creeps per wave
eliteCreepsInWave = currentWave; // Number of elite creeps equals wave number
}
creepsSpawned = 0;
eliteCreepsSpawned = 0;
waveCompleted = false;
waveText.setText('Wave: ' + currentWave + (bossWave ? ' (BOSS)' : ''));
}
// Player movement variables
var targetX = 1024;
var targetY = 1366;
var isMoving = false;
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
isMoving = true;
};
game.move = function (x, y, obj) {
if (isMoving) {
targetX = x;
targetY = y;
}
};
game.up = function (x, y, obj) {
isMoving = false;
};
game.update = function () {
// Process pickups FIRST to ensure upgrades always activate before any damage
for (var i = pickups.length - 1; i >= 0; i--) {
var pickup = pickups[i];
// Check pickup collection using both intersection and distance
var dx = pickup.x - player.x;
var dy = pickup.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collected = pickup.intersects(player) || distance < 50;
if (collected) {
// Immediately activate upgrade effect and calculate points
var pickupPoints = 150; // Default points
switch (pickup.type) {
case 'health':
// Check if player is at full health before healing
var wasAtFullHealth = player.health >= player.maxHealth;
player.heal(25);
// Double points if player was at full health when picking up health potion
if (wasAtFullHealth) {
pickupPoints = 300;
}
break;
case 'damage':
player.addDamage(5);
break;
case 'speed':
player.addSpeed(1);
break;
case 'shield':
player.activateShield();
break;
}
// Give points for collecting upgrades
gameScore += pickupPoints;
LK.setScore(LK.getScore() + pickupPoints);
LK.getSound('pickup').play();
pickup.destroy();
pickups.splice(i, 1);
} else if (pickup.lifetime <= 0) {
pickup.destroy();
pickups.splice(i, 1);
}
}
// Player movement
if (isMoving) {
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
player.x += dx / distance * player.moveSpeed;
player.y += dy / distance * player.moveSpeed;
}
}
// Automatic shooting at closest creep
if (creeps.length > 0 && player.canShoot()) {
var closestCreep = null;
var closestDistance = Infinity;
for (var i = 0; i < creeps.length; i++) {
var creep = creeps[i];
var dx = creep.x - player.x;
var dy = creep.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestCreep = creep;
}
}
if (closestCreep) {
var shotBullets = player.shoot(closestCreep.x, closestCreep.y);
if (shotBullets) {
for (var k = 0; k < shotBullets.length; k++) {
bullets.push(shotBullets[k]);
game.addChild(shotBullets[k]);
}
}
}
}
// Update shield system
if (player.shieldActive) {
player.shieldDuration--;
if (player.shieldDuration <= 0 || player.shieldHealth <= 0) {
player.shieldActive = false;
player.shieldHealth = 0;
player.shieldDuration = 0;
if (player.shieldAura) {
player.shieldAura.destroy();
player.shieldAura = null;
}
} else if (player.shieldAura) {
// Animate shield aura
player.shieldAura.rotation += 0.05;
// Flash when shield is about to expire (last 5 seconds)
if (player.shieldDuration < 300) {
player.shieldAura.alpha = 0.3 + 0.3 * Math.sin(LK.ticks * 0.3);
}
}
}
// Player defense is based on shield
player.defense = 0;
// Update player health display
healthText.setText('Health: ' + player.health);
armorText.setText('Armor: ' + player.defense);
scoreText.setText('Score: ' + gameScore);
// Update shield display
if (player.shieldActive) {
var shieldTimeLeft = Math.ceil(player.shieldDuration / 60);
shieldText.setText('Shield: ' + player.shieldHealth + '/' + player.shieldMaxHealth + ' (' + shieldTimeLeft + 's)');
} else {
shieldText.setText('');
}
// Update boss health bar
if (currentBoss && bossHealthBar.alpha > 0) {
var healthPercent = Math.round(currentBoss.health / currentBoss.maxHealth * 100);
bossHealthBar.setText('BOSS: ' + currentBoss.health + '/' + currentBoss.maxHealth + ' (' + healthPercent + '%)');
}
// Check game over
if (player.health <= 0) {
LK.showGameOver();
return;
}
// Wave management
if (!waveCompleted) {
if (bossWave) {
// Boss wave logic
if (!currentBoss && creeps.length === 0) {
spawnBoss();
}
// Check if boss wave is complete
if (currentBoss && creeps.length === 0) {
waveCompleted = true;
nextWaveTimer = 180; // 3 second break
currentBoss = null;
// Hide boss health bar
tween(bossHealthBar, {
alpha: 0
}, {
duration: 500
});
}
} else {
// Regular wave logic
// Spawn creeps
if (creepsSpawned < creepsInWave || eliteCreepsSpawned < eliteCreepsInWave) {
spawnTimer++;
if (spawnTimer >= 60) {
// Spawn every second
if (creepsSpawned < creepsInWave) {
spawnCreep();
creepsSpawned++;
} else if (eliteCreepsSpawned < eliteCreepsInWave) {
spawnEliteCreep();
eliteCreepsSpawned++;
}
spawnTimer = 0;
}
}
// Check if wave is complete
if (creepsSpawned >= creepsInWave && eliteCreepsSpawned >= eliteCreepsInWave && creeps.length === 0) {
waveCompleted = true;
nextWaveTimer = 180; // 3 second break
}
}
} else {
nextWaveTimer--;
if (nextWaveTimer <= 0) {
startNextWave();
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is off screen
if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-creep collisions
var hitCreep = false;
for (var j = creeps.length - 1; j >= 0; j--) {
var creep = creeps[j];
if (bullet.intersects(creep)) {
var isDead = creep.takeDamage(bullet.damage);
if (isDead) {
// Give points for killing creeps
var scoreValue;
if (creep instanceof BossCreep) {
scoreValue = 1000; // High score for boss
// Boss always drops multiple pickups
for (var p = 0; p < 3; p++) {
dropPickup(creep.x + (Math.random() - 0.5) * 100, creep.y + (Math.random() - 0.5) * 100);
}
} else if (creep instanceof EliteCreep) {
scoreValue = 200;
// Elite creeps have 50% chance to drop weapon damage upgrade
if (Math.random() < 0.5) {
var damagePickup = new Pickup('damage');
damagePickup.x = creep.x;
damagePickup.y = creep.y;
pickups.push(damagePickup);
game.addChild(damagePickup);
}
} else {
scoreValue = 100;
}
gameScore += scoreValue;
LK.setScore(LK.getScore() + scoreValue);
if (!(creep instanceof BossCreep)) {
dropPickup(creep.x, creep.y);
}
creep.destroy();
creeps.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
hitCreep = true;
break;
}
}
}
// Update creeps
for (var i = creeps.length - 1; i >= 0; i--) {
var creep = creeps[i];
// Check if creep reached player - adjust hit detection based on shield status
var canHitPlayer = false;
if (player.shieldActive) {
// When shield is active, creeps must be much closer to hit
var dx = creep.x - player.x;
var dy = creep.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
canHitPlayer = distance < 30; // Reduced hit distance when shield is active
} else {
// Normal hit detection when no shield
canHitPlayer = creep.hasReachedPlayer || creep.intersects(player);
}
if (canHitPlayer) {
if (!creep.hasReachedPlayer) {
player.takeDamage(creep.damage);
creep.hasReachedPlayer = true;
// Calculate bump direction (away from creep)
var bumpDx = player.x - creep.x;
var bumpDy = player.y - creep.y;
var bumpDistance = Math.sqrt(bumpDx * bumpDx + bumpDy * bumpDy);
if (bumpDistance > 0) {
var normalizedBumpX = bumpDx / bumpDistance;
var normalizedBumpY = bumpDy / bumpDistance;
var bumpStrength = 30; // How far to bump the player
// Bump the player away from the creep
tween(player, {
x: player.x + normalizedBumpX * bumpStrength,
y: player.y + normalizedBumpY * bumpStrength
}, {
duration: 200,
easing: tween.easeOut
});
}
}
// Don't destroy creep - it stays alive after hitting player
}
}
// Pickup processing moved to beginning of update loop
};
An space fighter jet viewed from the top. In-Game asset. 2d. High contrast. No shadows
An large circle of electric energy an circular force field shield. In-Game asset. 2d. High contrast. No shadows
An alien space mine that has spikes. In-Game asset. 2d. High contrast. No shadows
Alien mothership with spikes and glowing patterns and alien eye in middle. In-Game asset. 2d. High contrast. No shadows
an Symbol for an Shield Force shield of electric power. In-Game asset. 2d. High contrast. No shadows
An solid plasma ball of electrical force emanating power filled with reds yellows and green electric power. In-Game asset. 2d. High contrast. No shadows
Symbol for spaceship weapon upgrade with green arrow pointing up. In-Game asset. 2d. High contrast. No shadows
Alien spherical bullet with spikes emanating energy. In-Game asset. 2d. High contrast. No shadows
Symbol for a spaceship armor upgrade using an green arrow pointing up and white coloring. In-Game asset. 2d. High contrast. No shadows
symbol for spaceship speed upgrade increase. In-Game asset. 2d. High contrast. No shadows
Symbol for spaceship health increase with green arrow pointing up and red plus. In-Game asset. 2d. High contrast. No shadows
Symbol for space ship upgrade nova force field around it, with an white spaceship and orange electric circle surrounding it and an green arrow pointing up. In-Game asset. 2d. High contrast. No shadows
Symbol for an play button wide screen. In-Game asset. 2d. High contrast. No shadows
An alien space mine with spikes and red glowing center. In-Game asset. 2d. High contrast. No shadows
explosion in space. In-Game asset. 2d. High contrast. No shadows
Play again button. In-Game asset. 2d. High contrast. No shadows