/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Dragon = Container.expand(function () { var self = Container.call(this); var dragonGraphics = self.attachAsset('dragon', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.fireRate = 10; self.fireTimer = 0; self.speed = 8; self.update = function () { if (self.fireTimer > 0) { self.fireTimer--; } }; self.canShoot = function () { return self.fireTimer <= 0; }; self.shoot = function () { if (self.canShoot()) { var fireball = new Fireball(); fireball.x = self.x + 60; fireball.y = self.y; fireballs.push(fireball); game.addChild(fireball); self.fireTimer = self.fireRate; LK.getSound('shoot').play(); } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy1', { anchorX: 0.5, anchorY: 0.5 }); self.health = 10; self.speed = 3; self.damage = 10; self.hitCount = 0; self.maxHits = 5; self.dodgeCount = 0; self.maxDodges = 3; self.dodgeTimer = 0; self.isDodging = false; self.originalY = 0; self.fireRate = 90; self.fireTimer = 0; self.update = function () { self.x -= self.speed; if (self.fireTimer > 0) { self.fireTimer--; } if (self.dodgeTimer > 0) { self.dodgeTimer--; } // Trigger dodge randomly when enemy enters screen and hasn't used all dodges if (self.x < 1900 && self.x > 1800 && !self.isDodging && self.dodgeCount < self.maxDodges && self.dodgeTimer <= 0 && Math.random() < 0.4) { self.performDodge(); } // Shoot at dragon when in range if (self.x < 1600 && self.canShoot()) { self.shootAt(dragon.x, dragon.y); } }; self.canShoot = function () { return self.fireTimer <= 0; }; self.shootAt = function (targetX, targetY) { if (self.canShoot()) { var projectile = new EnemyProjectile(); projectile.x = self.x - 30; projectile.y = self.y; // Calculate direction towards dragon var deltaX = targetX - self.x; var deltaY = targetY - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 0) { projectile.directionX = deltaX / distance; projectile.directionY = deltaY / distance; } else { projectile.directionX = -1; projectile.directionY = 0; } enemyProjectiles.push(projectile); game.addChild(projectile); self.fireTimer = self.fireRate; LK.getSound('shoot').play(); } }; self.performDodge = function () { if (self.dodgeCount >= self.maxDodges || self.isDodging) return; self.isDodging = true; self.dodgeCount++; self.dodgeTimer = 120; // 2 second cooldown self.originalY = self.y; // Dodge up or down randomly var dodgeDirection = Math.random() < 0.5 ? -1 : 1; var dodgeDistance = 150; tween(self, { y: self.originalY + dodgeDistance * dodgeDirection }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Return to original position tween(self, { y: self.originalY }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { self.isDodging = false; } }); } }); }; self.takeDamage = function (damage) { self.hitCount++; LK.effects.flashObject(self, 0xff0000, 200); if (self.hitCount >= self.maxHits) { self.destroy(); return true; } return false; }; return self; }); var EnemyProjectile = Container.expand(function () { var self = Container.call(this); var projectileGraphics = self.attachAsset('enemyProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.damage = 10; self.directionX = -1; self.directionY = 0; self.update = function () { self.x += self.speed * self.directionX; self.y += self.speed * self.directionY; }; return self; }); var Explosion = Container.expand(function () { var self = Container.call(this); var explosionGraphics = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); self.lifespan = 30; self.maxLifespan = 30; self.update = function () { self.lifespan--; var progress = 1 - self.lifespan / self.maxLifespan; // Scale up then down if (progress < 0.3) { explosionGraphics.scaleX = explosionGraphics.scaleY = progress * 6; } else { explosionGraphics.scaleX = explosionGraphics.scaleY = (1 - progress) * 2; } // Fade out explosionGraphics.alpha = 1 - progress; // Remove when done if (self.lifespan <= 0) { self.destroy(); for (var i = explosions.length - 1; i >= 0; i--) { if (explosions[i] === self) { explosions.splice(i, 1); break; } } } }; return self; }); var FastEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy2', { anchorX: 0.5, anchorY: 0.5 }); self.health = 15; self.speed = 5; self.damage = 15; self.hitCount = 0; self.maxHits = 5; self.dodgeCount = 0; self.maxDodges = 3; self.dodgeTimer = 0; self.isDodging = false; self.originalY = 0; self.fireRate = 75; self.fireTimer = 0; self.update = function () { self.x -= self.speed; if (!self.isDodging) { self.y += Math.sin(self.x * 0.02) * 2; } if (self.fireTimer > 0) { self.fireTimer--; } if (self.dodgeTimer > 0) { self.dodgeTimer--; } // Trigger dodge randomly when enemy enters screen and hasn't used all dodges if (self.x < 1900 && self.x > 1800 && !self.isDodging && self.dodgeCount < self.maxDodges && self.dodgeTimer <= 0 && Math.random() < 0.5) { self.performDodge(); } // Shoot at dragon when in range if (self.x < 1700 && self.canShoot()) { self.shootAt(dragon.x, dragon.y); } }; self.canShoot = function () { return self.fireTimer <= 0; }; self.shootAt = function (targetX, targetY) { if (self.canShoot()) { var projectile = new EnemyProjectile(); projectile.x = self.x - 30; projectile.y = self.y; // Calculate direction towards dragon var deltaX = targetX - self.x; var deltaY = targetY - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 0) { projectile.directionX = deltaX / distance; projectile.directionY = deltaY / distance; } else { projectile.directionX = -1; projectile.directionY = 0; } enemyProjectiles.push(projectile); game.addChild(projectile); self.fireTimer = self.fireRate; LK.getSound('shoot').play(); } }; self.performDodge = function () { if (self.dodgeCount >= self.maxDodges || self.isDodging) return; self.isDodging = true; self.dodgeCount++; self.dodgeTimer = 100; // Shorter cooldown for fast enemy self.originalY = self.y; // Fast enemy does quicker, more agile dodges var dodgeDirection = Math.random() < 0.5 ? -1 : 1; var dodgeDistance = 180; tween(self, { y: self.originalY + dodgeDistance * dodgeDirection }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Return to original position tween(self, { y: self.originalY }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { self.isDodging = false; } }); } }); }; self.takeDamage = function (damage) { self.hitCount++; LK.effects.flashObject(self, 0xff0000, 200); if (self.hitCount >= self.maxHits) { self.destroy(); return true; } return false; }; return self; }); var Fireball = Container.expand(function () { var self = Container.call(this); var fireballGraphics = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 12; self.damage = 10; self.update = function () { self.x += self.speed; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerUpGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.type = 'damage'; // damage, firerate, speed self.update = function () { self.x -= self.speed; self.rotation += 0.1; }; return self; }); var ShootingEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy2', { anchorX: 0.5, anchorY: 0.5 }); self.health = 20; self.speed = 2; self.damage = 20; self.fireRate = 60; self.fireTimer = 0; self.hitCount = 0; self.maxHits = 5; self.dodgeCount = 0; self.maxDodges = 3; self.dodgeTimer = 0; self.isDodging = false; self.originalY = 0; self.update = function () { self.x -= self.speed; if (self.fireTimer > 0) { self.fireTimer--; } if (self.dodgeTimer > 0) { self.dodgeTimer--; } // Trigger dodge randomly when enemy enters screen and hasn't used all dodges if (self.x < 1900 && self.x > 1800 && !self.isDodging && self.dodgeCount < self.maxDodges && self.dodgeTimer <= 0 && Math.random() < 0.35) { self.performDodge(); } // Shoot at dragon every second (60 ticks) when in range if (self.x < 1800 && self.canShoot()) { self.shootAt(dragon.x, dragon.y); } }; self.canShoot = function () { return self.fireTimer <= 0; }; self.shootAt = function (targetX, targetY) { if (self.canShoot()) { // Define 4 cardinal directions: North, East, South, West var directions = [{ x: 0, y: -1 }, // North { x: 1, y: 0 }, // East { x: 0, y: 1 }, // South { x: -1, y: 0 } // West ]; // Fire projectiles in all 4 directions for (var i = 0; i < directions.length; i++) { var projectile = new EnemyProjectile(); projectile.x = self.x - 30; projectile.y = self.y; projectile.directionX = directions[i].x; projectile.directionY = directions[i].y; enemyProjectiles.push(projectile); game.addChild(projectile); } self.fireTimer = self.fireRate; LK.getSound('shoot').play(); } }; self.performDodge = function () { if (self.dodgeCount >= self.maxDodges || self.isDodging) return; self.isDodging = true; self.dodgeCount++; self.dodgeTimer = 150; // Longer cooldown for shooting enemy self.originalY = self.y; // Shooting enemy does defensive dodges var dodgeDirection = Math.random() < 0.5 ? -1 : 1; var dodgeDistance = 120; tween(self, { y: self.originalY + dodgeDistance * dodgeDirection }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Return to original position tween(self, { y: self.originalY }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { self.isDodging = false; } }); } }); }; self.takeDamage = function (damage) { self.hitCount++; LK.effects.flashObject(self, 0xff0000, 200); if (self.hitCount >= self.maxHits) { self.destroy(); return true; } return false; }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); var towerGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 1.0 }); self.health = 200; self.fireRate = 60; self.fireTimer = 0; self.range = 400; self.update = function () { if (self.fireTimer > 0) { self.fireTimer--; } // Only shoot when there are enemies present if (self.canShoot() && enemies.length > 0) { self.shootAt(0, 0); // Launch homing projectile } }; self.canShoot = function () { return self.fireTimer <= 0; }; self.shootAt = function (targetX, targetY) { if (self.canShoot()) { var projectile = new TowerProjectile(); projectile.x = self.x; projectile.y = self.y - 100; // Start with slight upward direction, homing will take over projectile.directionX = 0.1; projectile.directionY = -0.3; towerProjectiles.push(projectile); game.addChild(projectile); self.fireTimer = self.fireRate; LK.getSound('towerShoot').play(); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.destroy(); return true; } return false; }; return self; }); var TowerFragment = Container.expand(function () { var self = Container.call(this); var fragmentGraphics = self.attachAsset('towerFragment', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 8; self.directionX = 1; self.directionY = 0; self.lifespan = 60; // 1 second at 60fps self.update = function () { self.x += self.speed * self.directionX; self.y += self.speed * self.directionY; self.lifespan--; if (self.lifespan <= 0) { self.destroy(); } }; return self; }); var TowerProjectile = Container.expand(function () { var self = Container.call(this); var projectileGraphics = self.attachAsset('towerProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.damage = 15; self.directionX = 0; self.directionY = 0; self.target = null; self.homingStrength = 0.08; // How aggressively it homes in self.maxLifespan = 300; // Maximum time before auto-explode (5 seconds) self.lifespan = 300; self.hasExploded = false; self.update = function () { if (!self.hasExploded) { self.lifespan--; // Find nearest enemy to target var nearestEnemy = null; var nearestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var deltaX = enemy.x - self.x; var deltaY = enemy.y - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < nearestDistance) { nearestDistance = distance; nearestEnemy = enemy; } } // Update target and direction if (nearestEnemy) { self.target = nearestEnemy; var deltaX = self.target.x - self.x; var deltaY = self.target.y - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 0) { // Gradually adjust direction towards target var targetDirX = deltaX / distance; var targetDirY = deltaY / distance; self.directionX += (targetDirX - self.directionX) * self.homingStrength; self.directionY += (targetDirY - self.directionY) * self.homingStrength; // Normalize direction var dirLength = Math.sqrt(self.directionX * self.directionX + self.directionY * self.directionY); if (dirLength > 0) { self.directionX /= dirLength; self.directionY /= dirLength; } } } self.x += self.speed * self.directionX; self.y += self.speed * self.directionY; // Auto-explode after max lifespan or if very close to target if (self.lifespan <= 0 || self.target && nearestDistance < 30) { self.explode(); } } }; self.explode = function () { if (self.hasExploded) return; self.hasExploded = true; // Create explosion effect createExplosion(self.x, self.y); // Create fragments in all directions var fragmentCount = 8; for (var i = 0; i < fragmentCount; i++) { var angle = i / fragmentCount * Math.PI * 2; var fragment = new TowerFragment(); fragment.x = self.x; fragment.y = self.y; fragment.directionX = Math.cos(angle); fragment.directionY = Math.sin(angle); towerFragments.push(fragment); game.addChild(fragment); } // Remove this projectile self.destroy(); for (var j = towerProjectiles.length - 1; j >= 0; j--) { if (towerProjectiles[j] === self) { towerProjectiles.splice(j, 1); break; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb }); /**** * Game Code ****/ // Background var forestBg = game.addChild(LK.getAsset('forestBg', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.7 })); // Make background image sharp and detailed by disabling texture smoothing if (forestBg.texture && forestBg.texture.baseTexture) { forestBg.texture.baseTexture.scaleMode = 0; // SCALE_MODES.NEAREST for sharp pixels forestBg.texture.baseTexture.mipmap = false; // Disable mipmapping for sharper details } // Game variables var dragon = game.addChild(new Dragon()); var fireballs = []; var enemies = []; var powerups = []; var explosions = []; var enemyProjectiles = []; var towerProjectiles = []; var towerFragments = []; var enemySpawnTimer = 0; var powerUpSpawnTimer = 0; var waveLevel = 1; var gameSpeed = 1; var survivalTime = 0; var enemiesKilled = 0; // Add tower var tower = game.addChild(new Tower()); tower.x = 200; tower.y = 2500; // Add floating animation to tower function startTowerFloating() { // Float up tween(tower, { y: tower.y - 30 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Float down tween(tower, { y: tower.y + 30 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Restart the floating cycle startTowerFloating(); } }); } }); } // Start the floating animation startTowerFloating(); // Position initial elements dragon.x = 1024; dragon.y = 1366; // UI Elements var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 150; scoreTxt.y = 50; LK.gui.topLeft.addChild(scoreTxt); var timeTxt = new Text2('Time: 0s', { size: 60, fill: 0xFFFFFF }); timeTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timeTxt); var waveTxt = new Text2('Wave: 1', { size: 50, fill: 0xFFFFFF }); waveTxt.anchor.set(1, 0); LK.gui.topRight.addChild(waveTxt); function updateTime() { timeTxt.setText('Time: ' + Math.floor(survivalTime / 60) + 's'); } function updateScore() { scoreTxt.setText('Score: ' + LK.getScore()); } function updateWave() { waveTxt.setText('Wave: ' + waveLevel); } // Touch controls var isDragging = false; game.down = function (x, y, obj) { isDragging = true; dragon.shoot(); // Immediate visual feedback for better control feel LK.effects.flashObject(dragon, 0x00ff00, 200); // Smooth tween animation to touch position var targetX = Math.max(100, Math.min(1900, x)); var targetY = Math.max(100, Math.min(2600, y)); // Stop any existing movement tween tween.stop(dragon, { x: true, y: true }); // Animate smoothly to target position tween(dragon, { x: targetX, y: targetY }, { duration: 200, easing: tween.easeOut }); }; game.move = function (x, y, obj) { if (isDragging) { var targetX = Math.max(100, Math.min(1900, x)); var targetY = Math.max(100, Math.min(2600, y)); // Stop any existing movement tween for immediate responsiveness tween.stop(dragon, { x: true, y: true }); // Smooth tween animation to new position during drag tween(dragon, { x: targetX, y: targetY }, { duration: 150, easing: tween.easeOut }); } }; game.up = function (x, y, obj) { isDragging = false; }; // Spawn functions function spawnEnemy() { var enemy; var rand = Math.random(); if (rand < 0.2 + waveLevel * 0.05) { enemy = new ShootingEnemy(); } else if (rand < 0.4 + waveLevel * 0.1) { enemy = new FastEnemy(); } else { enemy = new Enemy(); } enemy.x = 2100; enemy.y = Math.random() * 2200 + 300; enemy.originalY = enemy.y; // Initialize original Y position for dodging enemies.push(enemy); game.addChild(enemy); } function spawnPowerUp() { var powerup = new PowerUp(); powerup.x = 2100; powerup.y = Math.random() * 2200 + 300; var rand = Math.random(); if (rand < 0.4) { powerup.type = 'damage'; } else if (rand < 0.7) { powerup.type = 'firerate'; } else { powerup.type = 'speed'; } powerups.push(powerup); game.addChild(powerup); } function createExplosion(x, y) { var explosion = new Explosion(); explosion.x = x; explosion.y = y; explosions.push(explosion); game.addChild(explosion); LK.getSound('explosion').play(); // Create additional tween effects for more dramatic explosion var particles = []; for (var i = 0; i < 5; i++) { var particle = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); particle.x = x; particle.y = y; game.addChild(particle); particles.push(particle); var randomX = x + (Math.random() - 0.5) * 200; var randomY = y + (Math.random() - 0.5) * 200; tween(particle, { x: randomX, y: randomY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } // Main game loop game.update = function () { // Update survival time survivalTime++; if (LK.ticks % 60 == 0) { updateTime(); } // Increase difficulty over time if (LK.ticks % 1800 == 0) { // Every 30 seconds waveLevel++; updateWave(); gameSpeed += 0.1; } // Spawn enemies enemySpawnTimer--; if (enemySpawnTimer <= 0) { spawnEnemy(); enemySpawnTimer = Math.max(300 - waveLevel * 8, 120); // Increased from 180 to 300, min from 60 to 120 } // Spawn power-ups occasionally powerUpSpawnTimer--; if (powerUpSpawnTimer <= 0) { if (Math.random() < 0.3) { spawnPowerUp(); } powerUpSpawnTimer = Math.random() * 600 + 300; // 5-15 seconds } // Update fireballs for (var i = fireballs.length - 1; i >= 0; i--) { var fireball = fireballs[i]; // Remove if off screen if (fireball.x > 2200) { fireball.destroy(); fireballs.splice(i, 1); continue; } // Check collision with enemy projectiles for (var k = enemyProjectiles.length - 1; k >= 0; k--) { var enemyProjectile = enemyProjectiles[k]; if (fireball.intersects(enemyProjectile)) { // Destroy both projectiles fireball.destroy(); fireballs.splice(i, 1); enemyProjectile.destroy(); enemyProjectiles.splice(k, 1); // Add small score bonus for defensive play LK.setScore(LK.getScore() + 5); updateScore(); LK.getSound('enemyHit').play(); // Create small explosion effect createExplosion(fireball.x, fireball.y); break; } } // Skip enemy collision check if fireball was already destroyed if (i >= fireballs.length) continue; // Check collision with enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (fireball.intersects(enemy)) { if (enemy.takeDamage(fireball.damage)) { createExplosion(enemy.x, enemy.y); enemies.splice(j, 1); enemiesKilled++; LK.setScore(LK.getScore() + 10); updateScore(); LK.getSound('enemyHit').play(); } fireball.destroy(); fireballs.splice(i, 1); break; } } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // Remove enemies that go off screen (left side) if (enemy.x < -100) { enemy.destroy(); enemies.splice(i, 1); continue; } // Check collision with dragon - game over if hit if (enemy.intersects(dragon)) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } // Update power-ups for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; // Remove if off screen if (powerup.x < -100) { powerup.destroy(); powerups.splice(i, 1); continue; } // Check collision with dragon if (powerup.intersects(dragon)) { if (powerup.type === 'damage') { dragon.damage = Math.min(dragon.damage + 5, 50); } else if (powerup.type === 'firerate') { dragon.fireRate = Math.max(dragon.fireRate - 2, 3); } else if (powerup.type === 'speed') { dragon.speed = Math.min(dragon.speed + 1, 15); } LK.getSound('powerUpCollect').play(); LK.effects.flashObject(dragon, 0xffd700, 500); powerup.destroy(); powerups.splice(i, 1); } } // Update enemy projectiles for (var i = enemyProjectiles.length - 1; i >= 0; i--) { var projectile = enemyProjectiles[i]; // Remove if off screen if (projectile.x < -100 || projectile.x > 2200 || projectile.y < -100 || projectile.y > 2800) { projectile.destroy(); enemyProjectiles.splice(i, 1); continue; } // Check collision with dragon - game over if hit if (projectile.intersects(dragon)) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } // Update tower projectiles for (var i = towerProjectiles.length - 1; i >= 0; i--) { var projectile = towerProjectiles[i]; // Remove if off screen (they should explode before this) if (projectile.x > 2200 || projectile.x < -100 || projectile.y > 2800 || projectile.y < -100) { projectile.destroy(); towerProjectiles.splice(i, 1); continue; } } // Update tower fragments for (var i = towerFragments.length - 1; i >= 0; i--) { var fragment = towerFragments[i]; // Remove if off screen or lifespan expired if (fragment.x > 2200 || fragment.x < -100 || fragment.y > 2800 || fragment.y < -100 || fragment.lifespan <= 0) { fragment.destroy(); towerFragments.splice(i, 1); continue; } // Check collision with enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (fragment.intersects(enemy)) { if (enemy.takeDamage(fragment.damage)) { createExplosion(enemy.x, enemy.y); enemies.splice(j, 1); enemiesKilled++; LK.setScore(LK.getScore() + 10); updateScore(); LK.getSound('enemyHit').play(); } fragment.destroy(); towerFragments.splice(i, 1); break; } } } // Check if enemies hit the tower for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.intersects(tower)) { if (tower.takeDamage(enemy.damage)) { // Tower destroyed - game over LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } enemy.destroy(); enemies.splice(i, 1); } } // Auto-shoot for dragon if (LK.ticks % 20 == 0) { dragon.shoot(); } // Smooth movement dampening when not actively dragging if (!isDragging) { // Apply slight dampening to make movement feel more natural dragon.x += (dragon.x - dragon.x) * 0.1; dragon.y += (dragon.y - dragon.y) * 0.1; } }; // Start background music LK.playMusic('bgmusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Dragon = Container.expand(function () {
var self = Container.call(this);
var dragonGraphics = self.attachAsset('dragon', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.fireRate = 10;
self.fireTimer = 0;
self.speed = 8;
self.update = function () {
if (self.fireTimer > 0) {
self.fireTimer--;
}
};
self.canShoot = function () {
return self.fireTimer <= 0;
};
self.shoot = function () {
if (self.canShoot()) {
var fireball = new Fireball();
fireball.x = self.x + 60;
fireball.y = self.y;
fireballs.push(fireball);
game.addChild(fireball);
self.fireTimer = self.fireRate;
LK.getSound('shoot').play();
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy1', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 10;
self.speed = 3;
self.damage = 10;
self.hitCount = 0;
self.maxHits = 5;
self.dodgeCount = 0;
self.maxDodges = 3;
self.dodgeTimer = 0;
self.isDodging = false;
self.originalY = 0;
self.fireRate = 90;
self.fireTimer = 0;
self.update = function () {
self.x -= self.speed;
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.dodgeTimer > 0) {
self.dodgeTimer--;
}
// Trigger dodge randomly when enemy enters screen and hasn't used all dodges
if (self.x < 1900 && self.x > 1800 && !self.isDodging && self.dodgeCount < self.maxDodges && self.dodgeTimer <= 0 && Math.random() < 0.4) {
self.performDodge();
}
// Shoot at dragon when in range
if (self.x < 1600 && self.canShoot()) {
self.shootAt(dragon.x, dragon.y);
}
};
self.canShoot = function () {
return self.fireTimer <= 0;
};
self.shootAt = function (targetX, targetY) {
if (self.canShoot()) {
var projectile = new EnemyProjectile();
projectile.x = self.x - 30;
projectile.y = self.y;
// Calculate direction towards dragon
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 0) {
projectile.directionX = deltaX / distance;
projectile.directionY = deltaY / distance;
} else {
projectile.directionX = -1;
projectile.directionY = 0;
}
enemyProjectiles.push(projectile);
game.addChild(projectile);
self.fireTimer = self.fireRate;
LK.getSound('shoot').play();
}
};
self.performDodge = function () {
if (self.dodgeCount >= self.maxDodges || self.isDodging) return;
self.isDodging = true;
self.dodgeCount++;
self.dodgeTimer = 120; // 2 second cooldown
self.originalY = self.y;
// Dodge up or down randomly
var dodgeDirection = Math.random() < 0.5 ? -1 : 1;
var dodgeDistance = 150;
tween(self, {
y: self.originalY + dodgeDistance * dodgeDirection
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return to original position
tween(self, {
y: self.originalY
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isDodging = false;
}
});
}
});
};
self.takeDamage = function (damage) {
self.hitCount++;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.hitCount >= self.maxHits) {
self.destroy();
return true;
}
return false;
};
return self;
});
var EnemyProjectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('enemyProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 10;
self.directionX = -1;
self.directionY = 0;
self.update = function () {
self.x += self.speed * self.directionX;
self.y += self.speed * self.directionY;
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 30;
self.maxLifespan = 30;
self.update = function () {
self.lifespan--;
var progress = 1 - self.lifespan / self.maxLifespan;
// Scale up then down
if (progress < 0.3) {
explosionGraphics.scaleX = explosionGraphics.scaleY = progress * 6;
} else {
explosionGraphics.scaleX = explosionGraphics.scaleY = (1 - progress) * 2;
}
// Fade out
explosionGraphics.alpha = 1 - progress;
// Remove when done
if (self.lifespan <= 0) {
self.destroy();
for (var i = explosions.length - 1; i >= 0; i--) {
if (explosions[i] === self) {
explosions.splice(i, 1);
break;
}
}
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 15;
self.speed = 5;
self.damage = 15;
self.hitCount = 0;
self.maxHits = 5;
self.dodgeCount = 0;
self.maxDodges = 3;
self.dodgeTimer = 0;
self.isDodging = false;
self.originalY = 0;
self.fireRate = 75;
self.fireTimer = 0;
self.update = function () {
self.x -= self.speed;
if (!self.isDodging) {
self.y += Math.sin(self.x * 0.02) * 2;
}
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.dodgeTimer > 0) {
self.dodgeTimer--;
}
// Trigger dodge randomly when enemy enters screen and hasn't used all dodges
if (self.x < 1900 && self.x > 1800 && !self.isDodging && self.dodgeCount < self.maxDodges && self.dodgeTimer <= 0 && Math.random() < 0.5) {
self.performDodge();
}
// Shoot at dragon when in range
if (self.x < 1700 && self.canShoot()) {
self.shootAt(dragon.x, dragon.y);
}
};
self.canShoot = function () {
return self.fireTimer <= 0;
};
self.shootAt = function (targetX, targetY) {
if (self.canShoot()) {
var projectile = new EnemyProjectile();
projectile.x = self.x - 30;
projectile.y = self.y;
// Calculate direction towards dragon
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 0) {
projectile.directionX = deltaX / distance;
projectile.directionY = deltaY / distance;
} else {
projectile.directionX = -1;
projectile.directionY = 0;
}
enemyProjectiles.push(projectile);
game.addChild(projectile);
self.fireTimer = self.fireRate;
LK.getSound('shoot').play();
}
};
self.performDodge = function () {
if (self.dodgeCount >= self.maxDodges || self.isDodging) return;
self.isDodging = true;
self.dodgeCount++;
self.dodgeTimer = 100; // Shorter cooldown for fast enemy
self.originalY = self.y;
// Fast enemy does quicker, more agile dodges
var dodgeDirection = Math.random() < 0.5 ? -1 : 1;
var dodgeDistance = 180;
tween(self, {
y: self.originalY + dodgeDistance * dodgeDirection
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return to original position
tween(self, {
y: self.originalY
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isDodging = false;
}
});
}
});
};
self.takeDamage = function (damage) {
self.hitCount++;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.hitCount >= self.maxHits) {
self.destroy();
return true;
}
return false;
};
return self;
});
var Fireball = Container.expand(function () {
var self = Container.call(this);
var fireballGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.damage = 10;
self.update = function () {
self.x += self.speed;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.type = 'damage'; // damage, firerate, speed
self.update = function () {
self.x -= self.speed;
self.rotation += 0.1;
};
return self;
});
var ShootingEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 20;
self.speed = 2;
self.damage = 20;
self.fireRate = 60;
self.fireTimer = 0;
self.hitCount = 0;
self.maxHits = 5;
self.dodgeCount = 0;
self.maxDodges = 3;
self.dodgeTimer = 0;
self.isDodging = false;
self.originalY = 0;
self.update = function () {
self.x -= self.speed;
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.dodgeTimer > 0) {
self.dodgeTimer--;
}
// Trigger dodge randomly when enemy enters screen and hasn't used all dodges
if (self.x < 1900 && self.x > 1800 && !self.isDodging && self.dodgeCount < self.maxDodges && self.dodgeTimer <= 0 && Math.random() < 0.35) {
self.performDodge();
}
// Shoot at dragon every second (60 ticks) when in range
if (self.x < 1800 && self.canShoot()) {
self.shootAt(dragon.x, dragon.y);
}
};
self.canShoot = function () {
return self.fireTimer <= 0;
};
self.shootAt = function (targetX, targetY) {
if (self.canShoot()) {
// Define 4 cardinal directions: North, East, South, West
var directions = [{
x: 0,
y: -1
},
// North
{
x: 1,
y: 0
},
// East
{
x: 0,
y: 1
},
// South
{
x: -1,
y: 0
} // West
];
// Fire projectiles in all 4 directions
for (var i = 0; i < directions.length; i++) {
var projectile = new EnemyProjectile();
projectile.x = self.x - 30;
projectile.y = self.y;
projectile.directionX = directions[i].x;
projectile.directionY = directions[i].y;
enemyProjectiles.push(projectile);
game.addChild(projectile);
}
self.fireTimer = self.fireRate;
LK.getSound('shoot').play();
}
};
self.performDodge = function () {
if (self.dodgeCount >= self.maxDodges || self.isDodging) return;
self.isDodging = true;
self.dodgeCount++;
self.dodgeTimer = 150; // Longer cooldown for shooting enemy
self.originalY = self.y;
// Shooting enemy does defensive dodges
var dodgeDirection = Math.random() < 0.5 ? -1 : 1;
var dodgeDistance = 120;
tween(self, {
y: self.originalY + dodgeDistance * dodgeDirection
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return to original position
tween(self, {
y: self.originalY
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isDodging = false;
}
});
}
});
};
self.takeDamage = function (damage) {
self.hitCount++;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.hitCount >= self.maxHits) {
self.destroy();
return true;
}
return false;
};
return self;
});
var Tower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 200;
self.fireRate = 60;
self.fireTimer = 0;
self.range = 400;
self.update = function () {
if (self.fireTimer > 0) {
self.fireTimer--;
}
// Only shoot when there are enemies present
if (self.canShoot() && enemies.length > 0) {
self.shootAt(0, 0); // Launch homing projectile
}
};
self.canShoot = function () {
return self.fireTimer <= 0;
};
self.shootAt = function (targetX, targetY) {
if (self.canShoot()) {
var projectile = new TowerProjectile();
projectile.x = self.x;
projectile.y = self.y - 100;
// Start with slight upward direction, homing will take over
projectile.directionX = 0.1;
projectile.directionY = -0.3;
towerProjectiles.push(projectile);
game.addChild(projectile);
self.fireTimer = self.fireRate;
LK.getSound('towerShoot').play();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
var TowerFragment = Container.expand(function () {
var self = Container.call(this);
var fragmentGraphics = self.attachAsset('towerFragment', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 8;
self.directionX = 1;
self.directionY = 0;
self.lifespan = 60; // 1 second at 60fps
self.update = function () {
self.x += self.speed * self.directionX;
self.y += self.speed * self.directionY;
self.lifespan--;
if (self.lifespan <= 0) {
self.destroy();
}
};
return self;
});
var TowerProjectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('towerProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 15;
self.directionX = 0;
self.directionY = 0;
self.target = null;
self.homingStrength = 0.08; // How aggressively it homes in
self.maxLifespan = 300; // Maximum time before auto-explode (5 seconds)
self.lifespan = 300;
self.hasExploded = false;
self.update = function () {
if (!self.hasExploded) {
self.lifespan--;
// Find nearest enemy to target
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var deltaX = enemy.x - self.x;
var deltaY = enemy.y - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Update target and direction
if (nearestEnemy) {
self.target = nearestEnemy;
var deltaX = self.target.x - self.x;
var deltaY = self.target.y - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 0) {
// Gradually adjust direction towards target
var targetDirX = deltaX / distance;
var targetDirY = deltaY / distance;
self.directionX += (targetDirX - self.directionX) * self.homingStrength;
self.directionY += (targetDirY - self.directionY) * self.homingStrength;
// Normalize direction
var dirLength = Math.sqrt(self.directionX * self.directionX + self.directionY * self.directionY);
if (dirLength > 0) {
self.directionX /= dirLength;
self.directionY /= dirLength;
}
}
}
self.x += self.speed * self.directionX;
self.y += self.speed * self.directionY;
// Auto-explode after max lifespan or if very close to target
if (self.lifespan <= 0 || self.target && nearestDistance < 30) {
self.explode();
}
}
};
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Create explosion effect
createExplosion(self.x, self.y);
// Create fragments in all directions
var fragmentCount = 8;
for (var i = 0; i < fragmentCount; i++) {
var angle = i / fragmentCount * Math.PI * 2;
var fragment = new TowerFragment();
fragment.x = self.x;
fragment.y = self.y;
fragment.directionX = Math.cos(angle);
fragment.directionY = Math.sin(angle);
towerFragments.push(fragment);
game.addChild(fragment);
}
// Remove this projectile
self.destroy();
for (var j = towerProjectiles.length - 1; j >= 0; j--) {
if (towerProjectiles[j] === self) {
towerProjectiles.splice(j, 1);
break;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb
});
/****
* Game Code
****/
// Background
var forestBg = game.addChild(LK.getAsset('forestBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.7
}));
// Make background image sharp and detailed by disabling texture smoothing
if (forestBg.texture && forestBg.texture.baseTexture) {
forestBg.texture.baseTexture.scaleMode = 0; // SCALE_MODES.NEAREST for sharp pixels
forestBg.texture.baseTexture.mipmap = false; // Disable mipmapping for sharper details
}
// Game variables
var dragon = game.addChild(new Dragon());
var fireballs = [];
var enemies = [];
var powerups = [];
var explosions = [];
var enemyProjectiles = [];
var towerProjectiles = [];
var towerFragments = [];
var enemySpawnTimer = 0;
var powerUpSpawnTimer = 0;
var waveLevel = 1;
var gameSpeed = 1;
var survivalTime = 0;
var enemiesKilled = 0;
// Add tower
var tower = game.addChild(new Tower());
tower.x = 200;
tower.y = 2500;
// Add floating animation to tower
function startTowerFloating() {
// Float up
tween(tower, {
y: tower.y - 30
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Float down
tween(tower, {
y: tower.y + 30
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the floating cycle
startTowerFloating();
}
});
}
});
}
// Start the floating animation
startTowerFloating();
// Position initial elements
dragon.x = 1024;
dragon.y = 1366;
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 150;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
var timeTxt = new Text2('Time: 0s', {
size: 60,
fill: 0xFFFFFF
});
timeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timeTxt);
var waveTxt = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFFFF
});
waveTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(waveTxt);
function updateTime() {
timeTxt.setText('Time: ' + Math.floor(survivalTime / 60) + 's');
}
function updateScore() {
scoreTxt.setText('Score: ' + LK.getScore());
}
function updateWave() {
waveTxt.setText('Wave: ' + waveLevel);
}
// Touch controls
var isDragging = false;
game.down = function (x, y, obj) {
isDragging = true;
dragon.shoot();
// Immediate visual feedback for better control feel
LK.effects.flashObject(dragon, 0x00ff00, 200);
// Smooth tween animation to touch position
var targetX = Math.max(100, Math.min(1900, x));
var targetY = Math.max(100, Math.min(2600, y));
// Stop any existing movement tween
tween.stop(dragon, {
x: true,
y: true
});
// Animate smoothly to target position
tween(dragon, {
x: targetX,
y: targetY
}, {
duration: 200,
easing: tween.easeOut
});
};
game.move = function (x, y, obj) {
if (isDragging) {
var targetX = Math.max(100, Math.min(1900, x));
var targetY = Math.max(100, Math.min(2600, y));
// Stop any existing movement tween for immediate responsiveness
tween.stop(dragon, {
x: true,
y: true
});
// Smooth tween animation to new position during drag
tween(dragon, {
x: targetX,
y: targetY
}, {
duration: 150,
easing: tween.easeOut
});
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Spawn functions
function spawnEnemy() {
var enemy;
var rand = Math.random();
if (rand < 0.2 + waveLevel * 0.05) {
enemy = new ShootingEnemy();
} else if (rand < 0.4 + waveLevel * 0.1) {
enemy = new FastEnemy();
} else {
enemy = new Enemy();
}
enemy.x = 2100;
enemy.y = Math.random() * 2200 + 300;
enemy.originalY = enemy.y; // Initialize original Y position for dodging
enemies.push(enemy);
game.addChild(enemy);
}
function spawnPowerUp() {
var powerup = new PowerUp();
powerup.x = 2100;
powerup.y = Math.random() * 2200 + 300;
var rand = Math.random();
if (rand < 0.4) {
powerup.type = 'damage';
} else if (rand < 0.7) {
powerup.type = 'firerate';
} else {
powerup.type = 'speed';
}
powerups.push(powerup);
game.addChild(powerup);
}
function createExplosion(x, y) {
var explosion = new Explosion();
explosion.x = x;
explosion.y = y;
explosions.push(explosion);
game.addChild(explosion);
LK.getSound('explosion').play();
// Create additional tween effects for more dramatic explosion
var particles = [];
for (var i = 0; i < 5; i++) {
var particle = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = x;
particle.y = y;
game.addChild(particle);
particles.push(particle);
var randomX = x + (Math.random() - 0.5) * 200;
var randomY = y + (Math.random() - 0.5) * 200;
tween(particle, {
x: randomX,
y: randomY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
}
// Main game loop
game.update = function () {
// Update survival time
survivalTime++;
if (LK.ticks % 60 == 0) {
updateTime();
}
// Increase difficulty over time
if (LK.ticks % 1800 == 0) {
// Every 30 seconds
waveLevel++;
updateWave();
gameSpeed += 0.1;
}
// Spawn enemies
enemySpawnTimer--;
if (enemySpawnTimer <= 0) {
spawnEnemy();
enemySpawnTimer = Math.max(300 - waveLevel * 8, 120); // Increased from 180 to 300, min from 60 to 120
}
// Spawn power-ups occasionally
powerUpSpawnTimer--;
if (powerUpSpawnTimer <= 0) {
if (Math.random() < 0.3) {
spawnPowerUp();
}
powerUpSpawnTimer = Math.random() * 600 + 300; // 5-15 seconds
}
// Update fireballs
for (var i = fireballs.length - 1; i >= 0; i--) {
var fireball = fireballs[i];
// Remove if off screen
if (fireball.x > 2200) {
fireball.destroy();
fireballs.splice(i, 1);
continue;
}
// Check collision with enemy projectiles
for (var k = enemyProjectiles.length - 1; k >= 0; k--) {
var enemyProjectile = enemyProjectiles[k];
if (fireball.intersects(enemyProjectile)) {
// Destroy both projectiles
fireball.destroy();
fireballs.splice(i, 1);
enemyProjectile.destroy();
enemyProjectiles.splice(k, 1);
// Add small score bonus for defensive play
LK.setScore(LK.getScore() + 5);
updateScore();
LK.getSound('enemyHit').play();
// Create small explosion effect
createExplosion(fireball.x, fireball.y);
break;
}
}
// Skip enemy collision check if fireball was already destroyed
if (i >= fireballs.length) continue;
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (fireball.intersects(enemy)) {
if (enemy.takeDamage(fireball.damage)) {
createExplosion(enemy.x, enemy.y);
enemies.splice(j, 1);
enemiesKilled++;
LK.setScore(LK.getScore() + 10);
updateScore();
LK.getSound('enemyHit').play();
}
fireball.destroy();
fireballs.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Remove enemies that go off screen (left side)
if (enemy.x < -100) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with dragon - game over if hit
if (enemy.intersects(dragon)) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
// Update power-ups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
// Remove if off screen
if (powerup.x < -100) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with dragon
if (powerup.intersects(dragon)) {
if (powerup.type === 'damage') {
dragon.damage = Math.min(dragon.damage + 5, 50);
} else if (powerup.type === 'firerate') {
dragon.fireRate = Math.max(dragon.fireRate - 2, 3);
} else if (powerup.type === 'speed') {
dragon.speed = Math.min(dragon.speed + 1, 15);
}
LK.getSound('powerUpCollect').play();
LK.effects.flashObject(dragon, 0xffd700, 500);
powerup.destroy();
powerups.splice(i, 1);
}
}
// Update enemy projectiles
for (var i = enemyProjectiles.length - 1; i >= 0; i--) {
var projectile = enemyProjectiles[i];
// Remove if off screen
if (projectile.x < -100 || projectile.x > 2200 || projectile.y < -100 || projectile.y > 2800) {
projectile.destroy();
enemyProjectiles.splice(i, 1);
continue;
}
// Check collision with dragon - game over if hit
if (projectile.intersects(dragon)) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
// Update tower projectiles
for (var i = towerProjectiles.length - 1; i >= 0; i--) {
var projectile = towerProjectiles[i];
// Remove if off screen (they should explode before this)
if (projectile.x > 2200 || projectile.x < -100 || projectile.y > 2800 || projectile.y < -100) {
projectile.destroy();
towerProjectiles.splice(i, 1);
continue;
}
}
// Update tower fragments
for (var i = towerFragments.length - 1; i >= 0; i--) {
var fragment = towerFragments[i];
// Remove if off screen or lifespan expired
if (fragment.x > 2200 || fragment.x < -100 || fragment.y > 2800 || fragment.y < -100 || fragment.lifespan <= 0) {
fragment.destroy();
towerFragments.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (fragment.intersects(enemy)) {
if (enemy.takeDamage(fragment.damage)) {
createExplosion(enemy.x, enemy.y);
enemies.splice(j, 1);
enemiesKilled++;
LK.setScore(LK.getScore() + 10);
updateScore();
LK.getSound('enemyHit').play();
}
fragment.destroy();
towerFragments.splice(i, 1);
break;
}
}
}
// Check if enemies hit the tower
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.intersects(tower)) {
if (tower.takeDamage(enemy.damage)) {
// Tower destroyed - game over
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
enemy.destroy();
enemies.splice(i, 1);
}
}
// Auto-shoot for dragon
if (LK.ticks % 20 == 0) {
dragon.shoot();
}
// Smooth movement dampening when not actively dragging
if (!isDragging) {
// Apply slight dampening to make movement feel more natural
dragon.x += (dragon.x - dragon.x) * 0.1;
dragon.y += (dragon.y - dragon.y) * 0.1;
}
};
// Start background music
LK.playMusic('bgmusic');
silver red row dragon mecha side scroller. In-Game asset. 2d. High contrast. No shadows
goblin spear ghotic fantasy warplane. side scroller. In-Game asset. 2d. High contrast. No shadows
anime image realistic ghotic medieval futuristic landscape from distance add clear blue sky day In-Game asset. 2d. High contrast. No shadows. In-Game asset. 2d. High contrast. No shadows