/****
* 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