User prompt
gelen düşmanlar birden fazla çeşitte yani 1 level da gelen düşmanların canı level arttıkça artıyor ayrıca farklı düşman türleride eklemeliyiz. her 10 seviyede bir Boss savaşı eklemeliyiz
User prompt
ölen düşmanlar yok olmalı ve gezene gitmeyi durdurmalı ayrıca bir patlama efekti kullanmalıyız ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
düşmanlar ölmüyor canlarını biraz azaltalım
User prompt
düşmanların ilk bölümdeki canı basein 2 saldırısında ölmeli
User prompt
attack range büyütelim biraz çok az şuanda
User prompt
we can see the attack range
User prompt
the base must be attack in first level his damage can kill be 2 hits after level the eneys are storenger
Code edit (1 edits merged)
Please save this source code
User prompt
Base Defense: Last Stand
Initial prompt
base defence game in a midde we have a base all levels introders attac kthe base if the enemy hits health is down we can upgrade some skills attack power attack speed attack range multi attack etc
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Base = Container.expand(function () { var self = Container.call(this); // Create a shadow for the base var baseShadow = self.attachAsset('base', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.08, scaleY: 1.08, alpha: 0.35 }); baseShadow.y += 18; baseShadow.x += 10; baseShadow.tint = 0x000000; // Main base graphics var baseGraphics = self.attachAsset('base', { anchorX: 0.5, anchorY: 0.5 }); var rangeIndicator = self.attachAsset('rangeIndicator', { anchorX: 0.5, anchorY: 0.5, alpha: 0.2 }); self.health = 100; self.maxHealth = 100; self.attackPower = 50; self.attackSpeed = 45; // frames between attacks self.attackRange = 400; self.multiAttack = 1; self.lastAttack = 0; self.updateRangeIndicator = function () { var scale = self.attackRange * 2 / 800; rangeIndicator.scaleX = scale; rangeIndicator.scaleY = scale; }; self.takeDamage = function (damage) { // Use provided damage or default to 20 for player bullets if (typeof damage !== 'number') damage = 20; self.health -= damage; LK.effects.flashObject(self, 0xff0000, 300); if (self.health <= 0) { LK.showGameOver(); } updateHealthDisplay(); }; self.canAttack = function () { return LK.ticks - self.lastAttack >= self.attackSpeed; }; self.attack = function () { if (!self.canAttack()) return; var targets = []; for (var i = 0; i < enemies.length && targets.length < self.multiAttack; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.attackRange) { targets.push(enemy); } } for (var j = 0; j < targets.length; j++) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.target = targets[j]; bullet.damage = self.attackPower; bullets.push(bullet); game.addChild(bullet); } if (targets.length > 0) { self.lastAttack = LK.ticks; LK.getSound('shoot').play(); } }; // Add orbit animation to base (planet-like orbit around center) self.orbitRadius = 220; // Distance from center of screen self.orbitAngle = Math.random() * Math.PI * 2; // Start at random angle self.orbitSpeed = 0.008 + Math.random() * 0.004; // Slower, smooth orbit self.rotationSpeed = 0.012 + Math.random() * 0.01; // Still spin in place for visual effect self.update = function () { // Orbit around the center of the screen self.orbitAngle += self.orbitSpeed; var centerX = 2048 / 2; var centerY = 2732 / 2; self.x = centerX + Math.cos(self.orbitAngle) * self.orbitRadius; self.y = centerY + Math.sin(self.orbitAngle) * self.orbitRadius; // Spin in place for visual effect baseGraphics.rotation += self.rotationSpeed; baseShadow.rotation += self.rotationSpeed; }; return self; }); var Boss = Container.expand(function () { var self = Container.call(this); // Add shadow for boss var bossShadow = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.10, scaleY: 1.10, alpha: 0.32 }); bossShadow.y += 20; bossShadow.x += 12; bossShadow.tint = 0x000000; var enemyGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.health = 200; self.maxHealth = 200; self.speed = 0.8; self.direction = { x: 0, y: 0 }; self.lastX = self.x; self.lastY = self.y; self.setTarget = 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.direction.x = dx / distance; self.direction.y = dy / distance; } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.health <= 0) { LK.getSound('enemyHit').play(); // Create explosion effect // Create particle explosion effect for (var p = 0; p < 15; p++) { var particle = LK.getAsset('boss', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); particle.x = self.x; particle.y = self.y; game.addChild(particle); var angle = p / 15 * Math.PI * 2; var speed = 150 + Math.random() * 100; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 600 + Math.random() * 400, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } tween(self, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Boss will be removed from enemies array in game.update } }); return true; // Boss died } return false; }; self.update = function () { // If boss is dead (health <= 0), don't move and prepare for removal if (self.health <= 0) { return true; // Remove dead boss } self.lastX = self.x; self.lastY = self.y; // Keep shadow under boss bossShadow.x = enemyGraphics.x + 12; bossShadow.y = enemyGraphics.y + 20; bossShadow.rotation = enemyGraphics.rotation; // Move toward base self.setTarget(base.x, base.y); self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Check if reached base var dx = self.x - base.x; var dy = self.y - base.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100) { // Boss deals double damage (4-10 total) var bossDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5 base.takeDamage(bossDamage); base.takeDamage(bossDamage); // Boss does double damage return true; // Boss reached base } return false; }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.target = null; self.damage = 10; self.update = function () { if (!self.target || !self.target.parent) { return true; // Remove bullet if target is gone } // Check collision with enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var enemyBullet = enemyBullets[i]; var dx = enemyBullet.x - self.x; var dy = enemyBullet.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 15) { // Destroy enemy bullet if (enemyBullet && enemyBullet.parent) { enemyBullet.destroy(); } enemyBullets.splice(i, 1); // Give points for destroying enemy bullet points += 5; updatePointsDisplay(); LK.getSound('hit').play(); // Flash effect LK.effects.flashObject(self, 0x00ff00, 200); return true; // Destroy this bullet too } } // Move toward target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { // Hit target var enemyDied = self.target.takeDamage(self.damage); if (enemyDied) { // Different point rewards for different enemy types if (self.target instanceof Boss) { points += 100; } else if (self.target instanceof EnemyTank) { points += 20; } else if (self.target instanceof EnemyFast) { points += 15; } else if (self.target instanceof EnemyShooter) { points += 25; } else { points += 10; } updatePointsDisplay(); } LK.getSound('hit').play(); return true; // Remove bullet } if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Check if bullet is still attached before returning if (!self.parent) { return true; // Remove bullet if it's already been removed } return false; }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); // Add shadow for enemy var enemyShadow = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.08, scaleY: 1.08, alpha: 0.35 }); enemyShadow.y += 10; enemyShadow.x += 6; enemyShadow.tint = 0x000000; var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 20; self.maxHealth = 20; self.speed = 1; self.direction = { x: 0, y: 0 }; self.lastX = self.x; self.lastY = self.y; self.setTarget = 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.direction.x = dx / distance; self.direction.y = dy / distance; } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.health <= 0) { LK.getSound('enemyHit').play(); // Create explosion effect // Create particle explosion effect for (var p = 0; p < 8; p++) { var particle = LK.getAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); particle.x = self.x; particle.y = self.y; game.addChild(particle); var angle = p / 8 * Math.PI * 2; var speed = 100 + Math.random() * 50; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 400 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } tween(self, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Enemy will be removed from enemies array in game.update } }); return true; // Enemy died } return false; }; self.update = function () { // If enemy is dead (health <= 0), don't move and prepare for removal if (self.health <= 0) { return true; // Remove dead enemy } self.lastX = self.x; self.lastY = self.y; // Keep shadow under enemy enemyShadow.x = enemyGraphics.x + 6; enemyShadow.y = enemyGraphics.y + 10; enemyShadow.rotation = enemyGraphics.rotation; // Move toward base self.setTarget(base.x, base.y); self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Check if reached base var dx = self.x - base.x; var dy = self.y - base.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { // Enemy deals 2-5 damage to base var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5 base.takeDamage(enemyDamage); // Defensive: Only remove if enemy has a parent (avoid undefined parent crash) if (self.parent) { self.parent.removeChild(self); } return true; // Enemy reached base } return false; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5, tint: 0xff6666 }); self.speed = 4; self.damage = 10; self.direction = { x: 0, y: 0 }; 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.direction.x = dx / distance; self.direction.y = dy / distance; } }; self.update = function () { // Move bullet self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Check if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { return true; // Remove bullet } // Check collision with base var dx = self.x - base.x; var dy = self.y - base.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { // Enemy bullet deals 2-5 damage to base var bulletDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5 base.takeDamage(bulletDamage); LK.getSound('hit').play(); return true; // Remove bullet } return false; }; return self; }); var EnemyFast = Container.expand(function () { var self = Container.call(this); // Add shadow for fast enemy var enemyShadow = self.attachAsset('enemyFast', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.08, scaleY: 1.08, alpha: 0.35 }); enemyShadow.y += 8; enemyShadow.x += 5; enemyShadow.tint = 0x000000; var enemyGraphics = self.attachAsset('enemyFast', { anchorX: 0.5, anchorY: 0.5 }); self.health = 5; self.maxHealth = 5; self.speed = 1.5; self.direction = { x: 0, y: 0 }; self.lastX = self.x; self.lastY = self.y; self.setTarget = 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.direction.x = dx / distance; self.direction.y = dy / distance; } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.health <= 0) { LK.getSound('enemyHit').play(); // Create explosion effect // Create particle explosion effect for (var p = 0; p < 6; p++) { var particle = LK.getAsset('enemyFast', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); particle.x = self.x; particle.y = self.y; game.addChild(particle); var angle = p / 6 * Math.PI * 2; var speed = 80 + Math.random() * 40; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 350 + Math.random() * 150, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } tween(self, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Enemy will be removed from enemies array in game.update } }); return true; // Enemy died } return false; }; self.update = function () { // If enemy is dead (health <= 0), don't move and prepare for removal if (self.health <= 0) { return true; // Remove dead enemy } self.lastX = self.x; self.lastY = self.y; // Keep shadow under fast enemy enemyShadow.x = enemyGraphics.x + 5; enemyShadow.y = enemyGraphics.y + 8; enemyShadow.rotation = enemyGraphics.rotation; // Move toward base self.setTarget(base.x, base.y); self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Check if reached base var dx = self.x - base.x; var dy = self.y - base.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { // Enemy deals 2-5 damage to base var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5 base.takeDamage(enemyDamage); return true; // Enemy reached base } return false; }; return self; }); var EnemyShooter = Container.expand(function () { var self = Container.call(this); // Add shadow for shooter enemy var enemyShadow = self.attachAsset('enemyShooter', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.08, scaleY: 1.08, alpha: 0.35 }); enemyShadow.y += 10; enemyShadow.x += 6; enemyShadow.tint = 0x000000; var enemyGraphics = self.attachAsset('enemyShooter', { anchorX: 0.5, anchorY: 0.5 }); self.health = 30; self.maxHealth = 30; self.speed = 0.3; // Very slow movement self.direction = { x: 0, y: 0 }; self.lastX = self.x; self.lastY = self.y; self.lastShot = 0; self.shootCooldown = 60; // 1 second between shots - much faster self.shootRange = 800; // Increased range for earlier shooting self.setTarget = 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.direction.x = dx / distance; self.direction.y = dy / distance; } }; self.shoot = function () { if (LK.ticks - self.lastShot < self.shootCooldown) return; var dx = base.x - self.x; var dy = base.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.shootRange) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.setDirection(base.x, base.y); enemyBullets.push(bullet); game.addChild(bullet); self.lastShot = LK.ticks; LK.effects.flashObject(self, 0xff6666, 200); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.health <= 0) { LK.getSound('enemyHit').play(); // Create explosion effect for (var p = 0; p < 8; p++) { var particle = LK.getAsset('enemyShooter', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); particle.x = self.x; particle.y = self.y; game.addChild(particle); var angle = p / 8 * Math.PI * 2; var speed = 100 + Math.random() * 50; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 400 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } tween(self, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Enemy will be removed from enemies array in game.update } }); return true; // Enemy died } return false; }; self.update = function () { // If enemy is dead, don't move and prepare for removal if (self.health <= 0) { return true; // Remove dead enemy } self.lastX = self.x; self.lastY = self.y; // Keep shadow under enemy enemyShadow.x = enemyGraphics.x + 6; enemyShadow.y = enemyGraphics.y + 10; enemyShadow.rotation = enemyGraphics.rotation; // Move toward base slowly self.setTarget(base.x, base.y); self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Try to shoot at base self.shoot(); // Check if reached base var dx = self.x - base.x; var dy = self.y - base.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { // Enemy deals 2-5 damage to base var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5 base.takeDamage(enemyDamage); return true; // Enemy reached base } return false; }; return self; }); // 3 seconds var EnemyTank = Container.expand(function () { var self = Container.call(this); // Add shadow for tank enemy var enemyShadow = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.08, scaleY: 1.08, alpha: 0.35 }); enemyShadow.y += 12; enemyShadow.x += 7; enemyShadow.tint = 0x000000; var enemyGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); self.health = 40; self.maxHealth = 40; self.speed = 0.5; self.direction = { x: 0, y: 0 }; self.lastX = self.x; self.lastY = self.y; self.setTarget = 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.direction.x = dx / distance; self.direction.y = dy / distance; } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); if (self.health <= 0) { LK.getSound('enemyHit').play(); // Create explosion effect // Create particle explosion effect for (var p = 0; p < 10; p++) { var particle = LK.getAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); particle.x = self.x; particle.y = self.y; game.addChild(particle); var angle = p / 10 * Math.PI * 2; var speed = 120 + Math.random() * 60; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500 + Math.random() * 300, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } tween(self, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Enemy will be removed from enemies array in game.update } }); return true; // Enemy died } return false; }; self.update = function () { // If enemy is dead (health <= 0), don't move and prepare for removal if (self.health <= 0) { return true; // Remove dead enemy } self.lastX = self.x; self.lastY = self.y; // Keep shadow under tank enemy enemyShadow.x = enemyGraphics.x + 7; enemyShadow.y = enemyGraphics.y + 12; enemyShadow.rotation = enemyGraphics.rotation; // Move toward base self.setTarget(base.x, base.y); self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Check if reached base var dx = self.x - base.x; var dy = self.y - base.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { // Enemy deals 2-5 damage to base var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5 base.takeDamage(enemyDamage); return true; // Enemy reached base } return false; }; return self; }); var Shield = Container.expand(function () { var self = Container.call(this); var shieldGraphics = LK.getAsset('shield', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.25, alpha: 0.8 }); self.addChild(shieldGraphics); self.radius = 250; // Distance from base center self.rotationSpeed = 0.02; // Rotation speed self.angle = 0; self.update = function () { // Rotate around the base self.angle += self.rotationSpeed; self.x = base.x + Math.cos(self.angle) * self.radius; self.y = base.y + Math.sin(self.angle) * self.radius; // Check collision with enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Shield collision radius if (distance < 50) { // Destroy enemy on contact enemy.takeDamage(enemy.health); LK.effects.flashObject(self, 0x00ff00, 200); } } }; return self; }); var UpgradeButton = Container.expand(function (type, cost, description, fontSize) { var self = Container.call(this); var buttonGraphics = self.attachAsset('upgradeButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, // Reduced width for side-by-side layout scaleY: 0.7 // Reduced height for compactness }); self.type = type; self.cost = cost; self.description = description; self.level = 0; self.maxLevel = 999; // Default no limit if (typeof fontSize !== "number" || fontSize < 10) fontSize = 36; var buttonText = new Text2(description + '\n' + cost + ' pts', { size: 64, // Match pointsText/waveText/healthText size fill: 0xFFFFFF, font: "bold 64px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" // Match style for readability }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.updateCost = function (newCost) { self.cost = newCost; if (self.level >= self.maxLevel) { buttonText.setText(self.description + '\nMAX LEVEL'); buttonGraphics.tint = 0x808080; } else { buttonText.setText(self.description + '\n' + newCost + ' pts'); } }; self.down = function (x, y, obj) { if (points >= self.cost && self.level < self.maxLevel) { points -= self.cost; updatePointsDisplay(); self.level++; switch (self.type) { case 'power': base.attackPower += 20; powerUpgradeCost = Math.floor(powerUpgradeCost * 1.5); self.updateCost(powerUpgradeCost); break; case 'speed': base.attackSpeed = Math.max(10, base.attackSpeed - 10); speedUpgradeCost = Math.floor(speedUpgradeCost * 1.5); self.updateCost(speedUpgradeCost); break; case 'range': base.attackRange += 100; base.updateRangeIndicator(); rangeUpgradeCost = Math.floor(rangeUpgradeCost * 1.5); self.updateCost(rangeUpgradeCost); break; case 'multi': base.multiAttack += 1; multiUpgradeCost = Math.floor(multiUpgradeCost * 2); self.updateCost(multiUpgradeCost); break; case 'shield': if (!shield) { shield = new Shield(); game.addChild(shield); self.visible = false; // Hide shield button after purchase } break; } LK.effects.flashObject(self, 0xffffff, 300); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ // Add background image behind all gameplay elements // Background image for the game (full screen, subtle, fits 2048x2732) var backgroundImage = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1, scaleY: 1 }); game.addChild(backgroundImage); var base = game.addChild(new Base()); base.x = 2048 / 2; base.y = 2732 / 2; base.updateRangeIndicator(); var enemies = []; var bullets = []; var enemyBullets = []; var points = 0; var wave = 1; var waveInProgress = false; var enemiesSpawned = 0; var enemiesPerWave = 5; var spawnTimer = 0; var waveStartDelay = 0; var shield = null; // Upgrade costs var powerUpgradeCost = 50; var speedUpgradeCost = 30; var rangeUpgradeCost = 40; var multiUpgradeCost = 100; var shieldUpgradeCost = 500; // UI Elements var healthText = new Text2('Health: 100', { size: 48, fill: 0xFFFFFF }); healthText.anchor.set(0, 0); healthText.x = 50; healthText.y = 150; LK.gui.topLeft.addChild(healthText); var pointsText = new Text2('Points: 0', { size: 64, fill: 0xFFFFFF }); pointsText.anchor.set(0.5, 0); pointsText.x = 0; pointsText.y = 120; LK.gui.top.addChild(pointsText); var waveText = new Text2('Wave: 1', { size: 48, fill: 0xFFFFFF }); waveText.anchor.set(1, 0); waveText.x = -50; waveText.y = 150; LK.gui.topRight.addChild(waveText); var statusText = new Text2('Prepare for Wave 1!', { size: 48, fill: 0xF39C12 }); statusText.anchor.set(0.5, 0); statusText.x = 0; statusText.y = 50; LK.gui.top.addChild(statusText); // New upgrade menu: horizontal, text/buttons styled to match health/wave/points, large and readable // Layout constants for new menu var upgradeMenuY = 2732 - 220; var upgradeMenuButtonCount = 5; var upgradeMenuButtonSpacing = 340; var upgradeMenuButtonStartX = (2048 - upgradeMenuButtonSpacing * (upgradeMenuButtonCount - 1)) / 2; // Button info: [type, cost, label, maxLevel, description] var upgradeMenuData = [['power', powerUpgradeCost, 'Güç', undefined, 'Saldırı gücünü artırır'], ['speed', speedUpgradeCost, 'Hız', undefined, 'Saldırı hızını artırır'], ['range', rangeUpgradeCost, 'Menzil', 4, 'Menzili artırır'], ['multi', multiUpgradeCost, 'Çoklu', 3, 'Çoklu atış sağlar'], ['shield', shieldUpgradeCost, 'Kalkan', 1, 'Kalkan ekler']]; // Store references for cost/level updates var powerButton, speedButton, rangeButton, multiButton, shieldButton; var upgradeMenuButtons = []; for (var i = 0; i < upgradeMenuButtonCount; i++) { var btnType = upgradeMenuData[i][0]; var btnCost = upgradeMenuData[i][1]; var btnLabel = upgradeMenuData[i][2]; var btnMaxLevel = upgradeMenuData[i][3]; var btnDesc = upgradeMenuData[i][4]; // Create button background using 'upgradeButton' asset, styled to match general UI var btn = new UpgradeButton(btnType, btnCost, btnLabel, Math.floor(LK.width / 18)); // Remove default children (graphics/text) for custom layout while (btn.children.length > 0) { var child = btn.children[0]; // Defensive: Only remove if child has a parent (avoid undefined parent crash) if (child && child.parent) { child.parent.removeChild(child); } else if (typeof child !== "undefined") { // Fallback: forcibly remove from children array if not attached btn.children.splice(0, 1); } else { // If child is undefined, break to avoid infinite loop break; } } var bg = btn.attachAsset('upgradeButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.1, scaleY: 1.0, alpha: 0.98 }); btn.children.unshift(bg); // Position horizontally btn.x = upgradeMenuButtonStartX + i * upgradeMenuButtonSpacing; btn.y = upgradeMenuY; // Add label (large, bold, white, matching pointsText style) var labelText = new Text2(btnLabel, { size: Math.floor(LK.width / 18), fill: 0xffffff, font: "bold " + Math.floor(LK.width / 18) + "px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); labelText.anchor.set(0.5, 1); labelText.x = 0; labelText.y = -10; btn.addChild(labelText); // Add cost (large, bold, yellow, matching pointsText style) var costText = new Text2(btnCost + ' pts', { size: Math.floor(LK.width / 22), fill: 0xf1c40f, font: "bold " + Math.floor(LK.width / 22) + "px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); costText.anchor.set(0.5, 0); costText.x = 0; costText.y = 10; btn.addChild(costText); // Add description (smaller, white, below cost) var descText = new Text2(btnDesc, { size: Math.floor(LK.width / 32), fill: 0xffffff, font: "bold " + Math.floor(LK.width / 32) + "px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); descText.anchor.set(0.5, 0); descText.x = 0; descText.y = 70; btn.addChild(descText); // Set maxLevel if needed if (typeof btnMaxLevel === "number") btn.maxLevel = btnMaxLevel; // Store reference for cost/level updates if (btnType === 'power') powerButton = btn; if (btnType === 'speed') speedButton = btn; if (btnType === 'range') rangeButton = btn; if (btnType === 'multi') multiButton = btn; if (btnType === 'shield') shieldButton = btn; upgradeMenuButtons.push(btn); game.addChild(btn); } // Patch UpgradeButton.updateCost to update the cost text inside the button UpgradeButton.prototype.updateCost = function (newCost) { this.cost = newCost; // Find costText (Text2) child for (var i = 0; i < this.children.length; i++) { var ch = this.children[i]; if (ch instanceof Text2 && ch.text && (ch.text.indexOf('pts') !== -1 || ch.text === 'MAX LEVEL')) { if (this.level >= this.maxLevel) { ch.setText('MAX LEVEL'); if (this.children[0] && this.children[0].tint !== undefined) this.children[0].tint = 0x808080; } else { ch.setText(newCost + ' pts'); if (this.children[0] && this.children[0].tint !== undefined) this.children[0].tint = 0xffffff; } } } }; function updateHealthDisplay() { healthText.setText('Health: ' + base.health); } function updatePointsDisplay() { pointsText.setText('Points: ' + points); } function startWave() { waveInProgress = true; enemiesSpawned = 0; // Boss waves have only 1 enemy (the boss) // Boss now appears every 5 waves (5, 10, 15, ...) if (wave % 5 === 0) { enemiesPerWave = 1; statusText.setText('BOSS WAVE ' + wave + ' - Prepare for battle!'); } else { enemiesPerWave = Math.floor(5 + Math.pow(wave, 1.3) * 2); statusText.setText('Wave ' + wave + ' - ' + enemiesPerWave + ' enemies incoming!'); } spawnTimer = 0; waveText.setText('Wave: ' + wave); } function spawnEnemy() { var enemy; // Boss every 5 waves if (wave % 5 === 0 && enemiesSpawned === 0) { enemy = new Boss(); // Boss number (1 for wave 5, 2 for wave 10, etc.) var bossNumber = Math.floor(wave / 5); // Each boss is stronger than the previous one enemy.health = 300 + (bossNumber - 1) * 400; enemy.maxHealth = enemy.health; enemy.speed = 1 + bossNumber * 0.15; } else { // Progressive enemy introduction based on wave var enemyType; if (wave === 1) { // Wave 1: Only basic red enemies enemyType = 0; } else if (wave <= 5) { // Waves 2-5: Basic enemies and tanks enemyType = Math.floor(Math.random() * 2); } else if (wave <= 10) { // Waves 6-10: Basic, tanks, and fast enemies enemyType = Math.floor(Math.random() * 3); } else { // Wave 11+: All enemy types including shooters enemyType = Math.floor(Math.random() * 4); } switch (enemyType) { case 0: enemy = new Enemy(); // Scale enemy stats with wave - more aggressive scaling enemy.health = 20 + Math.pow(wave - 1, 1.5) * 5; enemy.maxHealth = enemy.health; enemy.speed = 1 + wave * 0.2; break; case 1: enemy = new EnemyTank(); // Scale tank stats with wave - more aggressive scaling enemy.health = 60 + Math.pow(wave - 1, 1.5) * 12; enemy.maxHealth = enemy.health; enemy.speed = 0.5 + wave * 0.12; break; case 2: enemy = new EnemyFast(); // Scale fast enemy stats with wave - more aggressive scaling enemy.health = 10 + Math.pow(wave - 1, 1.5) * 2; enemy.maxHealth = enemy.health; enemy.speed = 2 + wave * 0.25; break; case 3: enemy = new EnemyShooter(); // Scale shooter stats with wave enemy.health = 30 + Math.pow(wave - 1, 1.5) * 8; enemy.maxHealth = enemy.health; enemy.speed = 0.3 + wave * 0.05; enemy.shootCooldown = Math.max(30, 90 - wave * 3); // Much faster shooting - starts at 1.5 seconds enemy.shootRange = 800; // Increased range so they shoot from farther away break; } } // Random spawn position at screen edges var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top enemy.x = Math.random() * 2048; enemy.y = -50; break; case 1: // Right enemy.x = 2048 + 50; enemy.y = Math.random() * 2732; break; case 2: // Bottom enemy.x = Math.random() * 2048; enemy.y = 2732 + 50; break; case 3: // Left enemy.x = -50; enemy.y = Math.random() * 2732; break; } enemies.push(enemy); game.addChild(enemy); enemiesSpawned++; } function endWave() { waveInProgress = false; wave++; waveStartDelay = 300; // 5 seconds delay statusText.setText('Wave ' + (wave - 1) + ' Complete! Next wave in 5 seconds...'); // Bonus points for surviving the wave points += wave * 10; updatePointsDisplay(); } game.down = function (x, y, obj) { // Manual shooting when tapping the screen if (waveInProgress) { base.attack(); } }; game.update = function () { // Handle wave progression if (!waveInProgress) { if (waveStartDelay > 0) { waveStartDelay--; if (waveStartDelay === 0) { startWave(); } } } else { // Spawn enemies if (enemiesSpawned < enemiesPerWave) { spawnTimer++; var spawnDelay = Math.max(10, 25 - Math.floor(wave / 2)); // Much faster spawning as waves progress if (spawnTimer >= spawnDelay) { spawnEnemy(); spawnTimer = 0; } } // Check if wave is complete if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) { endWave(); } } // Update shield if (shield) { shield.update(); } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var shouldRemove = enemy.update(); if (shouldRemove) { enemy.destroy(); enemies.splice(i, 1); } } // Update bullets for (var j = bullets.length - 1; j >= 0; j--) { var bullet = bullets[j]; var shouldRemove = bullet.update(); if (shouldRemove) { // Defensive: Only destroy if bullet has a parent (avoid undefined parent crash) if (bullet && bullet.parent) { bullet.destroy(); } bullets.splice(j, 1); } } // Update enemy bullets for (var k = enemyBullets.length - 1; k >= 0; k--) { var enemyBullet = enemyBullets[k]; var shouldRemove = enemyBullet.update(); if (shouldRemove) { // Defensive: Only destroy if bullet has a parent (avoid undefined parent crash) if (enemyBullet && enemyBullet.parent) { enemyBullet.destroy(); } enemyBullets.splice(k, 1); } } // Auto-attack if (waveInProgress) { base.attack(); } }; // Start first wave after delay waveStartDelay = 180;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Base = Container.expand(function () {
var self = Container.call(this);
// Create a shadow for the base
var baseShadow = self.attachAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.08,
scaleY: 1.08,
alpha: 0.35
});
baseShadow.y += 18;
baseShadow.x += 10;
baseShadow.tint = 0x000000;
// Main base graphics
var baseGraphics = self.attachAsset('base', {
anchorX: 0.5,
anchorY: 0.5
});
var rangeIndicator = self.attachAsset('rangeIndicator', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2
});
self.health = 100;
self.maxHealth = 100;
self.attackPower = 50;
self.attackSpeed = 45; // frames between attacks
self.attackRange = 400;
self.multiAttack = 1;
self.lastAttack = 0;
self.updateRangeIndicator = function () {
var scale = self.attackRange * 2 / 800;
rangeIndicator.scaleX = scale;
rangeIndicator.scaleY = scale;
};
self.takeDamage = function (damage) {
// Use provided damage or default to 20 for player bullets
if (typeof damage !== 'number') damage = 20;
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
LK.showGameOver();
}
updateHealthDisplay();
};
self.canAttack = function () {
return LK.ticks - self.lastAttack >= self.attackSpeed;
};
self.attack = function () {
if (!self.canAttack()) return;
var targets = [];
for (var i = 0; i < enemies.length && targets.length < self.multiAttack; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange) {
targets.push(enemy);
}
}
for (var j = 0; j < targets.length; j++) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = targets[j];
bullet.damage = self.attackPower;
bullets.push(bullet);
game.addChild(bullet);
}
if (targets.length > 0) {
self.lastAttack = LK.ticks;
LK.getSound('shoot').play();
}
};
// Add orbit animation to base (planet-like orbit around center)
self.orbitRadius = 220; // Distance from center of screen
self.orbitAngle = Math.random() * Math.PI * 2; // Start at random angle
self.orbitSpeed = 0.008 + Math.random() * 0.004; // Slower, smooth orbit
self.rotationSpeed = 0.012 + Math.random() * 0.01; // Still spin in place for visual effect
self.update = function () {
// Orbit around the center of the screen
self.orbitAngle += self.orbitSpeed;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
self.x = centerX + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = centerY + Math.sin(self.orbitAngle) * self.orbitRadius;
// Spin in place for visual effect
baseGraphics.rotation += self.rotationSpeed;
baseShadow.rotation += self.rotationSpeed;
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
// Add shadow for boss
var bossShadow = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.10,
scaleY: 1.10,
alpha: 0.32
});
bossShadow.y += 20;
bossShadow.x += 12;
bossShadow.tint = 0x000000;
var enemyGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.speed = 0.8;
self.direction = {
x: 0,
y: 0
};
self.lastX = self.x;
self.lastY = self.y;
self.setTarget = 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.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.health <= 0) {
LK.getSound('enemyHit').play();
// Create explosion effect
// Create particle explosion effect
for (var p = 0; p < 15; p++) {
var particle = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
particle.x = self.x;
particle.y = self.y;
game.addChild(particle);
var angle = p / 15 * Math.PI * 2;
var speed = 150 + Math.random() * 100;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
tween(self, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Boss will be removed from enemies array in game.update
}
});
return true; // Boss died
}
return false;
};
self.update = function () {
// If boss is dead (health <= 0), don't move and prepare for removal
if (self.health <= 0) {
return true; // Remove dead boss
}
self.lastX = self.x;
self.lastY = self.y;
// Keep shadow under boss
bossShadow.x = enemyGraphics.x + 12;
bossShadow.y = enemyGraphics.y + 20;
bossShadow.rotation = enemyGraphics.rotation;
// Move toward base
self.setTarget(base.x, base.y);
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Check if reached base
var dx = self.x - base.x;
var dy = self.y - base.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
// Boss deals double damage (4-10 total)
var bossDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5
base.takeDamage(bossDamage);
base.takeDamage(bossDamage); // Boss does double damage
return true; // Boss reached base
}
return false;
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.target = null;
self.damage = 10;
self.update = function () {
if (!self.target || !self.target.parent) {
return true; // Remove bullet if target is gone
}
// Check collision with enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var enemyBullet = enemyBullets[i];
var dx = enemyBullet.x - self.x;
var dy = enemyBullet.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15) {
// Destroy enemy bullet
if (enemyBullet && enemyBullet.parent) {
enemyBullet.destroy();
}
enemyBullets.splice(i, 1);
// Give points for destroying enemy bullet
points += 5;
updatePointsDisplay();
LK.getSound('hit').play();
// Flash effect
LK.effects.flashObject(self, 0x00ff00, 200);
return true; // Destroy this bullet too
}
}
// Move toward target
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
// Hit target
var enemyDied = self.target.takeDamage(self.damage);
if (enemyDied) {
// Different point rewards for different enemy types
if (self.target instanceof Boss) {
points += 100;
} else if (self.target instanceof EnemyTank) {
points += 20;
} else if (self.target instanceof EnemyFast) {
points += 15;
} else if (self.target instanceof EnemyShooter) {
points += 25;
} else {
points += 10;
}
updatePointsDisplay();
}
LK.getSound('hit').play();
return true; // Remove bullet
}
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Check if bullet is still attached before returning
if (!self.parent) {
return true; // Remove bullet if it's already been removed
}
return false;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Add shadow for enemy
var enemyShadow = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.08,
scaleY: 1.08,
alpha: 0.35
});
enemyShadow.y += 10;
enemyShadow.x += 6;
enemyShadow.tint = 0x000000;
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 20;
self.maxHealth = 20;
self.speed = 1;
self.direction = {
x: 0,
y: 0
};
self.lastX = self.x;
self.lastY = self.y;
self.setTarget = 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.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.health <= 0) {
LK.getSound('enemyHit').play();
// Create explosion effect
// Create particle explosion effect
for (var p = 0; p < 8; p++) {
var particle = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = self.x;
particle.y = self.y;
game.addChild(particle);
var angle = p / 8 * Math.PI * 2;
var speed = 100 + Math.random() * 50;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 400 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Enemy will be removed from enemies array in game.update
}
});
return true; // Enemy died
}
return false;
};
self.update = function () {
// If enemy is dead (health <= 0), don't move and prepare for removal
if (self.health <= 0) {
return true; // Remove dead enemy
}
self.lastX = self.x;
self.lastY = self.y;
// Keep shadow under enemy
enemyShadow.x = enemyGraphics.x + 6;
enemyShadow.y = enemyGraphics.y + 10;
enemyShadow.rotation = enemyGraphics.rotation;
// Move toward base
self.setTarget(base.x, base.y);
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Check if reached base
var dx = self.x - base.x;
var dy = self.y - base.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Enemy deals 2-5 damage to base
var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5
base.takeDamage(enemyDamage);
// Defensive: Only remove if enemy has a parent (avoid undefined parent crash)
if (self.parent) {
self.parent.removeChild(self);
}
return true; // Enemy reached base
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff6666
});
self.speed = 4;
self.damage = 10;
self.direction = {
x: 0,
y: 0
};
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.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
self.update = function () {
// Move bullet
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Check if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
return true; // Remove bullet
}
// Check collision with base
var dx = self.x - base.x;
var dy = self.y - base.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
// Enemy bullet deals 2-5 damage to base
var bulletDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5
base.takeDamage(bulletDamage);
LK.getSound('hit').play();
return true; // Remove bullet
}
return false;
};
return self;
});
var EnemyFast = Container.expand(function () {
var self = Container.call(this);
// Add shadow for fast enemy
var enemyShadow = self.attachAsset('enemyFast', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.08,
scaleY: 1.08,
alpha: 0.35
});
enemyShadow.y += 8;
enemyShadow.x += 5;
enemyShadow.tint = 0x000000;
var enemyGraphics = self.attachAsset('enemyFast', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 5;
self.maxHealth = 5;
self.speed = 1.5;
self.direction = {
x: 0,
y: 0
};
self.lastX = self.x;
self.lastY = self.y;
self.setTarget = 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.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.health <= 0) {
LK.getSound('enemyHit').play();
// Create explosion effect
// Create particle explosion effect
for (var p = 0; p < 6; p++) {
var particle = LK.getAsset('enemyFast', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = self.x;
particle.y = self.y;
game.addChild(particle);
var angle = p / 6 * Math.PI * 2;
var speed = 80 + Math.random() * 40;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 350 + Math.random() * 150,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Enemy will be removed from enemies array in game.update
}
});
return true; // Enemy died
}
return false;
};
self.update = function () {
// If enemy is dead (health <= 0), don't move and prepare for removal
if (self.health <= 0) {
return true; // Remove dead enemy
}
self.lastX = self.x;
self.lastY = self.y;
// Keep shadow under fast enemy
enemyShadow.x = enemyGraphics.x + 5;
enemyShadow.y = enemyGraphics.y + 8;
enemyShadow.rotation = enemyGraphics.rotation;
// Move toward base
self.setTarget(base.x, base.y);
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Check if reached base
var dx = self.x - base.x;
var dy = self.y - base.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Enemy deals 2-5 damage to base
var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5
base.takeDamage(enemyDamage);
return true; // Enemy reached base
}
return false;
};
return self;
});
var EnemyShooter = Container.expand(function () {
var self = Container.call(this);
// Add shadow for shooter enemy
var enemyShadow = self.attachAsset('enemyShooter', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.08,
scaleY: 1.08,
alpha: 0.35
});
enemyShadow.y += 10;
enemyShadow.x += 6;
enemyShadow.tint = 0x000000;
var enemyGraphics = self.attachAsset('enemyShooter', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.maxHealth = 30;
self.speed = 0.3; // Very slow movement
self.direction = {
x: 0,
y: 0
};
self.lastX = self.x;
self.lastY = self.y;
self.lastShot = 0;
self.shootCooldown = 60; // 1 second between shots - much faster
self.shootRange = 800; // Increased range for earlier shooting
self.setTarget = 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.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
self.shoot = function () {
if (LK.ticks - self.lastShot < self.shootCooldown) return;
var dx = base.x - self.x;
var dy = base.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.shootRange) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.setDirection(base.x, base.y);
enemyBullets.push(bullet);
game.addChild(bullet);
self.lastShot = LK.ticks;
LK.effects.flashObject(self, 0xff6666, 200);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.health <= 0) {
LK.getSound('enemyHit').play();
// Create explosion effect
for (var p = 0; p < 8; p++) {
var particle = LK.getAsset('enemyShooter', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = self.x;
particle.y = self.y;
game.addChild(particle);
var angle = p / 8 * Math.PI * 2;
var speed = 100 + Math.random() * 50;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 400 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Enemy will be removed from enemies array in game.update
}
});
return true; // Enemy died
}
return false;
};
self.update = function () {
// If enemy is dead, don't move and prepare for removal
if (self.health <= 0) {
return true; // Remove dead enemy
}
self.lastX = self.x;
self.lastY = self.y;
// Keep shadow under enemy
enemyShadow.x = enemyGraphics.x + 6;
enemyShadow.y = enemyGraphics.y + 10;
enemyShadow.rotation = enemyGraphics.rotation;
// Move toward base slowly
self.setTarget(base.x, base.y);
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Try to shoot at base
self.shoot();
// Check if reached base
var dx = self.x - base.x;
var dy = self.y - base.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Enemy deals 2-5 damage to base
var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5
base.takeDamage(enemyDamage);
return true; // Enemy reached base
}
return false;
};
return self;
});
// 3 seconds
var EnemyTank = Container.expand(function () {
var self = Container.call(this);
// Add shadow for tank enemy
var enemyShadow = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.08,
scaleY: 1.08,
alpha: 0.35
});
enemyShadow.y += 12;
enemyShadow.x += 7;
enemyShadow.tint = 0x000000;
var enemyGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 40;
self.maxHealth = 40;
self.speed = 0.5;
self.direction = {
x: 0,
y: 0
};
self.lastX = self.x;
self.lastY = self.y;
self.setTarget = 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.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.health <= 0) {
LK.getSound('enemyHit').play();
// Create explosion effect
// Create particle explosion effect
for (var p = 0; p < 10; p++) {
var particle = LK.getAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
particle.x = self.x;
particle.y = self.y;
game.addChild(particle);
var angle = p / 10 * Math.PI * 2;
var speed = 120 + Math.random() * 60;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Enemy will be removed from enemies array in game.update
}
});
return true; // Enemy died
}
return false;
};
self.update = function () {
// If enemy is dead (health <= 0), don't move and prepare for removal
if (self.health <= 0) {
return true; // Remove dead enemy
}
self.lastX = self.x;
self.lastY = self.y;
// Keep shadow under tank enemy
enemyShadow.x = enemyGraphics.x + 7;
enemyShadow.y = enemyGraphics.y + 12;
enemyShadow.rotation = enemyGraphics.rotation;
// Move toward base
self.setTarget(base.x, base.y);
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Check if reached base
var dx = self.x - base.x;
var dy = self.y - base.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Enemy deals 2-5 damage to base
var enemyDamage = 2 + Math.floor(Math.random() * 4); // Random between 2-5
base.takeDamage(enemyDamage);
return true; // Enemy reached base
}
return false;
};
return self;
});
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.25,
alpha: 0.8
});
self.addChild(shieldGraphics);
self.radius = 250; // Distance from base center
self.rotationSpeed = 0.02; // Rotation speed
self.angle = 0;
self.update = function () {
// Rotate around the base
self.angle += self.rotationSpeed;
self.x = base.x + Math.cos(self.angle) * self.radius;
self.y = base.y + Math.sin(self.angle) * self.radius;
// Check collision with enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Shield collision radius
if (distance < 50) {
// Destroy enemy on contact
enemy.takeDamage(enemy.health);
LK.effects.flashObject(self, 0x00ff00, 200);
}
}
};
return self;
});
var UpgradeButton = Container.expand(function (type, cost, description, fontSize) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
// Reduced width for side-by-side layout
scaleY: 0.7 // Reduced height for compactness
});
self.type = type;
self.cost = cost;
self.description = description;
self.level = 0;
self.maxLevel = 999; // Default no limit
if (typeof fontSize !== "number" || fontSize < 10) fontSize = 36;
var buttonText = new Text2(description + '\n' + cost + ' pts', {
size: 64,
// Match pointsText/waveText/healthText size
fill: 0xFFFFFF,
font: "bold 64px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" // Match style for readability
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.updateCost = function (newCost) {
self.cost = newCost;
if (self.level >= self.maxLevel) {
buttonText.setText(self.description + '\nMAX LEVEL');
buttonGraphics.tint = 0x808080;
} else {
buttonText.setText(self.description + '\n' + newCost + ' pts');
}
};
self.down = function (x, y, obj) {
if (points >= self.cost && self.level < self.maxLevel) {
points -= self.cost;
updatePointsDisplay();
self.level++;
switch (self.type) {
case 'power':
base.attackPower += 20;
powerUpgradeCost = Math.floor(powerUpgradeCost * 1.5);
self.updateCost(powerUpgradeCost);
break;
case 'speed':
base.attackSpeed = Math.max(10, base.attackSpeed - 10);
speedUpgradeCost = Math.floor(speedUpgradeCost * 1.5);
self.updateCost(speedUpgradeCost);
break;
case 'range':
base.attackRange += 100;
base.updateRangeIndicator();
rangeUpgradeCost = Math.floor(rangeUpgradeCost * 1.5);
self.updateCost(rangeUpgradeCost);
break;
case 'multi':
base.multiAttack += 1;
multiUpgradeCost = Math.floor(multiUpgradeCost * 2);
self.updateCost(multiUpgradeCost);
break;
case 'shield':
if (!shield) {
shield = new Shield();
game.addChild(shield);
self.visible = false; // Hide shield button after purchase
}
break;
}
LK.effects.flashObject(self, 0xffffff, 300);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Add background image behind all gameplay elements
// Background image for the game (full screen, subtle, fits 2048x2732)
var backgroundImage = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
});
game.addChild(backgroundImage);
var base = game.addChild(new Base());
base.x = 2048 / 2;
base.y = 2732 / 2;
base.updateRangeIndicator();
var enemies = [];
var bullets = [];
var enemyBullets = [];
var points = 0;
var wave = 1;
var waveInProgress = false;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var spawnTimer = 0;
var waveStartDelay = 0;
var shield = null;
// Upgrade costs
var powerUpgradeCost = 50;
var speedUpgradeCost = 30;
var rangeUpgradeCost = 40;
var multiUpgradeCost = 100;
var shieldUpgradeCost = 500;
// UI Elements
var healthText = new Text2('Health: 100', {
size: 48,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
healthText.x = 50;
healthText.y = 150;
LK.gui.topLeft.addChild(healthText);
var pointsText = new Text2('Points: 0', {
size: 64,
fill: 0xFFFFFF
});
pointsText.anchor.set(0.5, 0);
pointsText.x = 0;
pointsText.y = 120;
LK.gui.top.addChild(pointsText);
var waveText = new Text2('Wave: 1', {
size: 48,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = -50;
waveText.y = 150;
LK.gui.topRight.addChild(waveText);
var statusText = new Text2('Prepare for Wave 1!', {
size: 48,
fill: 0xF39C12
});
statusText.anchor.set(0.5, 0);
statusText.x = 0;
statusText.y = 50;
LK.gui.top.addChild(statusText);
// New upgrade menu: horizontal, text/buttons styled to match health/wave/points, large and readable
// Layout constants for new menu
var upgradeMenuY = 2732 - 220;
var upgradeMenuButtonCount = 5;
var upgradeMenuButtonSpacing = 340;
var upgradeMenuButtonStartX = (2048 - upgradeMenuButtonSpacing * (upgradeMenuButtonCount - 1)) / 2;
// Button info: [type, cost, label, maxLevel, description]
var upgradeMenuData = [['power', powerUpgradeCost, 'Güç', undefined, 'Saldırı gücünü artırır'], ['speed', speedUpgradeCost, 'Hız', undefined, 'Saldırı hızını artırır'], ['range', rangeUpgradeCost, 'Menzil', 4, 'Menzili artırır'], ['multi', multiUpgradeCost, 'Çoklu', 3, 'Çoklu atış sağlar'], ['shield', shieldUpgradeCost, 'Kalkan', 1, 'Kalkan ekler']];
// Store references for cost/level updates
var powerButton, speedButton, rangeButton, multiButton, shieldButton;
var upgradeMenuButtons = [];
for (var i = 0; i < upgradeMenuButtonCount; i++) {
var btnType = upgradeMenuData[i][0];
var btnCost = upgradeMenuData[i][1];
var btnLabel = upgradeMenuData[i][2];
var btnMaxLevel = upgradeMenuData[i][3];
var btnDesc = upgradeMenuData[i][4];
// Create button background using 'upgradeButton' asset, styled to match general UI
var btn = new UpgradeButton(btnType, btnCost, btnLabel, Math.floor(LK.width / 18));
// Remove default children (graphics/text) for custom layout
while (btn.children.length > 0) {
var child = btn.children[0];
// Defensive: Only remove if child has a parent (avoid undefined parent crash)
if (child && child.parent) {
child.parent.removeChild(child);
} else if (typeof child !== "undefined") {
// Fallback: forcibly remove from children array if not attached
btn.children.splice(0, 1);
} else {
// If child is undefined, break to avoid infinite loop
break;
}
}
var bg = btn.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.0,
alpha: 0.98
});
btn.children.unshift(bg);
// Position horizontally
btn.x = upgradeMenuButtonStartX + i * upgradeMenuButtonSpacing;
btn.y = upgradeMenuY;
// Add label (large, bold, white, matching pointsText style)
var labelText = new Text2(btnLabel, {
size: Math.floor(LK.width / 18),
fill: 0xffffff,
font: "bold " + Math.floor(LK.width / 18) + "px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
labelText.anchor.set(0.5, 1);
labelText.x = 0;
labelText.y = -10;
btn.addChild(labelText);
// Add cost (large, bold, yellow, matching pointsText style)
var costText = new Text2(btnCost + ' pts', {
size: Math.floor(LK.width / 22),
fill: 0xf1c40f,
font: "bold " + Math.floor(LK.width / 22) + "px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
costText.anchor.set(0.5, 0);
costText.x = 0;
costText.y = 10;
btn.addChild(costText);
// Add description (smaller, white, below cost)
var descText = new Text2(btnDesc, {
size: Math.floor(LK.width / 32),
fill: 0xffffff,
font: "bold " + Math.floor(LK.width / 32) + "px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
descText.anchor.set(0.5, 0);
descText.x = 0;
descText.y = 70;
btn.addChild(descText);
// Set maxLevel if needed
if (typeof btnMaxLevel === "number") btn.maxLevel = btnMaxLevel;
// Store reference for cost/level updates
if (btnType === 'power') powerButton = btn;
if (btnType === 'speed') speedButton = btn;
if (btnType === 'range') rangeButton = btn;
if (btnType === 'multi') multiButton = btn;
if (btnType === 'shield') shieldButton = btn;
upgradeMenuButtons.push(btn);
game.addChild(btn);
}
// Patch UpgradeButton.updateCost to update the cost text inside the button
UpgradeButton.prototype.updateCost = function (newCost) {
this.cost = newCost;
// Find costText (Text2) child
for (var i = 0; i < this.children.length; i++) {
var ch = this.children[i];
if (ch instanceof Text2 && ch.text && (ch.text.indexOf('pts') !== -1 || ch.text === 'MAX LEVEL')) {
if (this.level >= this.maxLevel) {
ch.setText('MAX LEVEL');
if (this.children[0] && this.children[0].tint !== undefined) this.children[0].tint = 0x808080;
} else {
ch.setText(newCost + ' pts');
if (this.children[0] && this.children[0].tint !== undefined) this.children[0].tint = 0xffffff;
}
}
}
};
function updateHealthDisplay() {
healthText.setText('Health: ' + base.health);
}
function updatePointsDisplay() {
pointsText.setText('Points: ' + points);
}
function startWave() {
waveInProgress = true;
enemiesSpawned = 0;
// Boss waves have only 1 enemy (the boss)
// Boss now appears every 5 waves (5, 10, 15, ...)
if (wave % 5 === 0) {
enemiesPerWave = 1;
statusText.setText('BOSS WAVE ' + wave + ' - Prepare for battle!');
} else {
enemiesPerWave = Math.floor(5 + Math.pow(wave, 1.3) * 2);
statusText.setText('Wave ' + wave + ' - ' + enemiesPerWave + ' enemies incoming!');
}
spawnTimer = 0;
waveText.setText('Wave: ' + wave);
}
function spawnEnemy() {
var enemy;
// Boss every 5 waves
if (wave % 5 === 0 && enemiesSpawned === 0) {
enemy = new Boss();
// Boss number (1 for wave 5, 2 for wave 10, etc.)
var bossNumber = Math.floor(wave / 5);
// Each boss is stronger than the previous one
enemy.health = 300 + (bossNumber - 1) * 400;
enemy.maxHealth = enemy.health;
enemy.speed = 1 + bossNumber * 0.15;
} else {
// Progressive enemy introduction based on wave
var enemyType;
if (wave === 1) {
// Wave 1: Only basic red enemies
enemyType = 0;
} else if (wave <= 5) {
// Waves 2-5: Basic enemies and tanks
enemyType = Math.floor(Math.random() * 2);
} else if (wave <= 10) {
// Waves 6-10: Basic, tanks, and fast enemies
enemyType = Math.floor(Math.random() * 3);
} else {
// Wave 11+: All enemy types including shooters
enemyType = Math.floor(Math.random() * 4);
}
switch (enemyType) {
case 0:
enemy = new Enemy();
// Scale enemy stats with wave - more aggressive scaling
enemy.health = 20 + Math.pow(wave - 1, 1.5) * 5;
enemy.maxHealth = enemy.health;
enemy.speed = 1 + wave * 0.2;
break;
case 1:
enemy = new EnemyTank();
// Scale tank stats with wave - more aggressive scaling
enemy.health = 60 + Math.pow(wave - 1, 1.5) * 12;
enemy.maxHealth = enemy.health;
enemy.speed = 0.5 + wave * 0.12;
break;
case 2:
enemy = new EnemyFast();
// Scale fast enemy stats with wave - more aggressive scaling
enemy.health = 10 + Math.pow(wave - 1, 1.5) * 2;
enemy.maxHealth = enemy.health;
enemy.speed = 2 + wave * 0.25;
break;
case 3:
enemy = new EnemyShooter();
// Scale shooter stats with wave
enemy.health = 30 + Math.pow(wave - 1, 1.5) * 8;
enemy.maxHealth = enemy.health;
enemy.speed = 0.3 + wave * 0.05;
enemy.shootCooldown = Math.max(30, 90 - wave * 3); // Much faster shooting - starts at 1.5 seconds
enemy.shootRange = 800; // Increased range so they shoot from farther away
break;
}
}
// Random spawn position at screen edges
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2048 + 50;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2732 + 50;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
break;
}
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function endWave() {
waveInProgress = false;
wave++;
waveStartDelay = 300; // 5 seconds delay
statusText.setText('Wave ' + (wave - 1) + ' Complete! Next wave in 5 seconds...');
// Bonus points for surviving the wave
points += wave * 10;
updatePointsDisplay();
}
game.down = function (x, y, obj) {
// Manual shooting when tapping the screen
if (waveInProgress) {
base.attack();
}
};
game.update = function () {
// Handle wave progression
if (!waveInProgress) {
if (waveStartDelay > 0) {
waveStartDelay--;
if (waveStartDelay === 0) {
startWave();
}
}
} else {
// Spawn enemies
if (enemiesSpawned < enemiesPerWave) {
spawnTimer++;
var spawnDelay = Math.max(10, 25 - Math.floor(wave / 2)); // Much faster spawning as waves progress
if (spawnTimer >= spawnDelay) {
spawnEnemy();
spawnTimer = 0;
}
}
// Check if wave is complete
if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
endWave();
}
}
// Update shield
if (shield) {
shield.update();
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var shouldRemove = enemy.update();
if (shouldRemove) {
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update bullets
for (var j = bullets.length - 1; j >= 0; j--) {
var bullet = bullets[j];
var shouldRemove = bullet.update();
if (shouldRemove) {
// Defensive: Only destroy if bullet has a parent (avoid undefined parent crash)
if (bullet && bullet.parent) {
bullet.destroy();
}
bullets.splice(j, 1);
}
}
// Update enemy bullets
for (var k = enemyBullets.length - 1; k >= 0; k--) {
var enemyBullet = enemyBullets[k];
var shouldRemove = enemyBullet.update();
if (shouldRemove) {
// Defensive: Only destroy if bullet has a parent (avoid undefined parent crash)
if (enemyBullet && enemyBullet.parent) {
enemyBullet.destroy();
}
enemyBullets.splice(k, 1);
}
}
// Auto-attack
if (waveInProgress) {
base.attack();
}
};
// Start first wave after delay
waveStartDelay = 180;
thin range circle. In-Game asset. 2d. High contrast. No shadows
yellow meteor ball no flame effect. In-Game asset. 2d. High contrast. No shadows
purple meteor ball no flame effect. In-Game asset. 2d. High contrast. No shadows
red meteor mall no flame effect. In-Game asset. 2d. High contrast. No shadows
colorful meteor ball no flame effect. In-Game asset. 2d. High contrast. No shadows
green button empty. In-Game asset. 2d. High contrast. No shadows
yuvarlak mermi roket şeklinde. In-Game asset. 2d. High contrast. No shadows
circle shape starship. In-Game asset. 2d. High contrast. No shadows