/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (damage, target, assetType) {
var self = Container.call(this);
var bulletAsset = assetType || 'bullet';
var bulletGraphics = self.attachAsset(bulletAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = damage || 10;
self.target = target;
self.speed = 8;
self.update = function () {
if (!self.target || !self.target.parent) {
self.destroy();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.target.takeDamage(self.damage);
// If this is a slow bullet (iceball), apply slow effect
if (bulletAsset === 'iceball') {
self.target.slowed = true;
self.target.slowDuration = 240; // 4 seconds at 60fps
// Apply ice tint effect to show enemy is slowed
tween(self.target, {
tint: 0x87CEEB
}, {
duration: 200
});
}
// If this is a splash bullet, create persistent water damage area
if (self.isSplash) {
// Apply initial splash damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var splashDx = enemy.x - self.target.x;
var splashDy = enemy.y - self.target.y;
var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
if (splashDistance <= 80 && enemy !== self.target) {
enemy.takeDamage(Math.floor(self.damage * 0.5)); // Half damage for splash
}
}
// Only create persistent water damage area if flag is set (after 10 shots)
if (self.createWaterDamage) {
var waterDamage = new WaterDamage(self.target.x, self.target.y, Math.floor(self.damage * 0.3));
waterDamageAreas.push(waterDamage);
game.addChild(waterDamage);
}
}
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed * gameSpeed;
self.y += Math.sin(angle) * self.speed * gameSpeed;
// Add rainbow color cycling to bullets for colorful effect
var rainbowColors = [0xFF0000, 0xFF8C00, 0xFFD700, 0x00FF00, 0x00BFFF, 0x8A2BE2];
var colorIndex = Math.floor(LK.ticks / 10 % rainbowColors.length);
bulletGraphics.tint = rainbowColors[colorIndex];
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
self.enemyType = enemyType || 'basic';
self.pathIndex = 0;
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.reward = 10;
self.slowed = false;
self.slowDuration = 0;
var enemyGraphics;
if (self.enemyType === 'basic') {
enemyGraphics = self.attachAsset('basicEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 10 waves
var healthBonus = Math.floor(wave / 10) * 10; // +10 health per 10 waves
self.health = 30 + healthBonus;
self.maxHealth = 30 + healthBonus;
self.speed = 2;
self.reward = 10;
} else if (self.enemyType === 'fast') {
enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 11 waves
var healthBonus = Math.floor(wave / 11) * 20; // +20 health per 11 waves
self.health = 80 + healthBonus;
self.maxHealth = 80 + healthBonus;
self.speed = 4;
self.reward = 15;
} else if (self.enemyType === 'tank') {
enemyGraphics = self.attachAsset('tankEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 25 waves
var healthBonus = Math.floor(wave / 25) * 100; // +100 health per 25 waves
self.health = 400 + healthBonus;
self.maxHealth = 400 + healthBonus;
self.speed = 1;
self.reward = 25;
} else if (self.enemyType === 'gold') {
enemyGraphics = self.attachAsset('goldEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
self.speed = 3;
self.reward = 100; // High reward!
}
// Create health bar background
var healthBarBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1
});
healthBarBg.y = -enemyGraphics.height / 2 - 20;
healthBarBg.tint = 0x000000;
self.addChild(healthBarBg);
// Create health bar foreground
var healthBarFg = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1
});
healthBarFg.y = -enemyGraphics.height / 2 - 20;
healthBarFg.tint = 0x00FF00;
self.addChild(healthBarFg);
// Create health text to display current health as number
var healthText = new Text2(self.health.toString(), {
size: 30,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.y = -enemyGraphics.height / 2 - 20;
self.addChild(healthText);
self.healthBarBg = healthBarBg;
self.healthBarFg = healthBarFg;
self.healthText = healthText;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
self.healthBarFg.scaleX = 0.4 * healthPercent;
// Update health text to show current health number
self.healthText.setText(Math.max(0, self.health).toString());
// Change color based on health
if (healthPercent > 0.6) {
self.healthBarFg.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
self.healthBarFg.tint = 0xFFFF00; // Yellow
} else {
self.healthBarFg.tint = 0xFF0000; // Red
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
// Add colorful damage flash effect
var damageFlashColors = [0xFF4444, 0xFF8844, 0xFFFF44, 0x44FF44, 0x4444FF, 0xFF44FF];
var flashColor = damageFlashColors[Math.floor(Math.random() * damageFlashColors.length)];
tween(enemyGraphics, {
tint: flashColor,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 180
});
}
});
LK.getSound('enemyHit').play();
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Basic enemies give 2 coins, others use their reward value
var coinReward = self.enemyType === 'basic' ? 2 : self.reward;
coins += coinReward;
enemiesKilled++;
killStreak++;
LK.getSound('enemyDeath').play();
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
updateUI();
};
self.update = function () {
if (self.slowed) {
self.slowDuration--;
if (self.slowDuration <= 0) {
self.slowed = false;
// Remove ice tint when slow effect expires
tween(self, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
if (self.pathIndex >= pathPoints.length - 1) {
// Game over immediately when any enemy reaches the end
showCustomGameOver();
return;
}
var currentSpeed = self.slowed ? self.speed * 0.5 : self.speed;
currentSpeed *= gameSpeed; // Apply game speed multiplier
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * currentSpeed;
self.y += Math.sin(angle) * currentSpeed;
}
};
// Initialize health bar display
self.updateHealthBar();
return self;
});
var Tower = Container.expand(function (towerType) {
var self = Container.call(this);
self.towerType = towerType || 'basic';
self.level = 1;
self.range = 120;
self.damage = 10;
self.fireRate = 60;
self.cost = 50;
self.upgradeCost = 30;
self.lastShot = 0;
self.shotCount = 0; // Track number of shots for splash tower
var towerGraphics;
if (self.towerType === 'basic') {
towerGraphics = self.attachAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.range = 620;
self.fireRate = 60;
self.cost = 100;
} else if (self.towerType === 'splash') {
towerGraphics = self.attachAsset('splashTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.range = 600;
self.fireRate = 90;
self.cost = splashTowerCost;
} else if (self.towerType === 'slow') {
towerGraphics = self.attachAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.range = 640;
self.fireRate = 30;
self.cost = slowTowerCost;
} else if (self.towerType === 'sniper') {
towerGraphics = self.attachAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 100;
self.range = 1000;
self.fireRate = 300;
self.cost = sniperTowerCost;
}
self.canShoot = function () {
return LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed);
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
// Add colorful muzzle flash effect when shooting
var flashColors = {
'basic': 0xFFD700,
'splash': 0x00AAFF,
'slow': 0x87CEEB,
'sniper': 0xFF4500
};
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2,
tint: flashColors[self.towerType] || 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
if (self.towerType === 'basic') {
// Fire single bullet for basic tower
var bullet = new Bullet(self.damage, target);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'splash') {
// Splash tower fires waterball bullets and leaves watercharc damage
var bullet = new Bullet(self.damage, target, 'waterball');
bullet.x = self.x;
bullet.y = self.y;
bullet.isSplash = true; // Mark as splash bullet
self.shotCount++; // Increment shot counter
// Only create water damage after 10 shots
if (self.shotCount >= 10) {
bullet.createWaterDamage = true;
self.shotCount = 0; // Reset counter
}
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'slow') {
// Slow tower fires iceball bullets
var bullet = new Bullet(self.damage, target, 'iceball');
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'sniper') {
// Sniper tower fires sniperball bullets
var bullet = new Bullet(self.damage, target, 'sniperball');
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else {
// Other tower types fire single bullet
var bullet = new Bullet(self.damage, target);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
}
self.lastShot = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (coins >= self.upgradeCost && self.level < 3) {
coins -= self.upgradeCost;
self.level++;
self.damage = Math.floor(self.damage * 1.5);
self.range += 20;
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
updateUI();
}
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
self.down = function (x, y, obj) {
selectedTower = self;
showTowerMenu = true;
updateUI();
};
return self;
});
var WaterDamage = Container.expand(function (x, y, damage) {
var self = Container.call(this);
var waterGraphics = self.attachAsset('watercharc', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.damage = damage || 5; // Damage per tick
self.radius = 80; // Damage radius
self.lifetime = 300; // 5 seconds at 60fps
self.damageInterval = 30; // Damage every 0.5 seconds
self.lastDamageTick = 0;
waterGraphics.alpha = 0.7;
waterGraphics.tint = 0x00AAFF;
// Animate the water area appearance
waterGraphics.scaleX = 0.5;
waterGraphics.scaleY = 0.5;
tween(waterGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
self.update = function () {
self.lifetime--;
// Apply damage to enemies in range at intervals
if (LK.ticks - self.lastDamageTick >= self.damageInterval) {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.radius) {
enemy.takeDamage(self.damage);
}
}
self.lastDamageTick = LK.ticks;
}
// Fade out as lifetime decreases
if (self.lifetime < 60) {
// Start fading in last second
waterGraphics.alpha = 0.7 * (self.lifetime / 60);
}
// Remove when lifetime expires
if (self.lifetime <= 0) {
self.destroy();
// Remove from waterDamageAreas array
for (var j = waterDamageAreas.length - 1; j >= 0; j--) {
if (waterDamageAreas[j] === self) {
waterDamageAreas.splice(j, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8b4513 // Brown color
});
/****
* Game Code
****/
// Game state management
var gameState = 'home'; // 'home', 'playing'
var homeScreen = null;
var gridSize = 100;
var gridWidth = 20;
var gridHeight = 27;
var pathPoints = [{
x: 0,
y: 400
}, {
x: 300,
y: 400
}, {
x: 300,
y: 800
}, {
x: 700,
y: 800
}, {
x: 700,
y: 1200
}, {
x: 1100,
y: 1200
}, {
x: 1100,
y: 1600
}, {
x: 1500,
y: 1600
}, {
x: 1500,
y: 2000
}, {
x: 2048,
y: 2000
}];
var gameGrid = [];
var towers = [];
var enemies = [];
var bullets = [];
var waterDamageAreas = [];
var coins = 200;
var lives = 20;
var wave = 1;
var enemiesKilled = 0;
var killStreak = 0;
var lastLives = 20;
var enemiesSpawned = 0;
var enemiesPerWave = 10;
var spawnTimer = 0;
var waveInProgress = false;
var selectedTower = null;
var showTowerMenu = false;
var towerMenuButtons = [];
var slowTowerCost = 500; // Track dynamic slow tower cost
var splashTowerCost = 350; // Track dynamic splash tower cost
var sniperTowerCost = 1000; // Track dynamic sniper tower cost
// Create grid
for (var y = 0; y < gridHeight; y++) {
gameGrid[y] = [];
for (var x = 0; x < gridWidth; x++) {
gameGrid[y][x] = {
x: x * gridSize + 50,
y: y * gridSize + 50,
occupied: false,
isPath: false
};
}
}
// Mark path tiles
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var steps = Math.max(Math.abs(end.x - start.x), Math.abs(end.y - start.y)) / gridSize;
for (var step = 0; step <= steps; step++) {
var x = Math.floor((start.x + (end.x - start.x) * step / steps) / gridSize);
var y = Math.floor((start.y + (end.y - start.y) * step / steps) / gridSize);
if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) {
gameGrid[y][x].isPath = true;
}
}
}
// Create visual grid
for (var y = 0; y < gridHeight; y++) {
for (var x = 0; x < gridWidth; x++) {
var tile;
if (gameGrid[y][x].isPath) {
tile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
tile = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5
});
}
tile.x = gameGrid[y][x].x;
tile.y = gameGrid[y][x].y;
tile.alpha = 0.3;
game.addChild(tile);
}
}
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 60,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
LK.gui.topRight.addChild(coinsText);
// Add continuous rainbow animation to coins text
function animateCoinsText() {
var rainbowColors = [0xFFD700, 0xFF6B35, 0xF7931E, 0xFFD700, 0x00FF7F, 0x1E90FF, 0x9370DB];
var colorIndex = Math.floor(LK.ticks / 30 % rainbowColors.length);
tween(coinsText, {
tint: rainbowColors[colorIndex],
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
onFinish: function onFinish() {
tween(coinsText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
}
var livesText = new Text2('Lives: ' + lives, {
size: 50,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 0);
LK.gui.topRight.addChild(livesText);
var waveText = new Text2('Wave: ' + wave, {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var startWaveButton = new Text2('๐ START WAVE ๐', {
size: 80,
fill: 0x00FF00
});
startWaveButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(startWaveButton);
// Add colorful tween animation to make it more eye-catching
function animateStartButton() {
tween(startWaveButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onFinish() {
tween(startWaveButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
// Show home screen instead of starting immediately
createHomeScreen();
// Tower selection buttons
var basicTowerButton = new Text2('Basic ($100)', {
size: 80,
fill: 0xFFFFFF
});
basicTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(basicTowerButton);
var splashTowerButton = new Text2('Splash ($' + splashTowerCost + ')', {
size: 80,
fill: 0xFFFFFF
});
splashTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(splashTowerButton);
var slowTowerButton = new Text2('Slow ($' + slowTowerCost + ')', {
size: 80,
fill: 0xFFFFFF
});
slowTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(slowTowerButton);
var sniperTowerButton = new Text2('Sniper ($' + sniperTowerCost + ')', {
size: 80,
fill: 0xFFFFFF
});
sniperTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(sniperTowerButton);
// Create shovel button for tower elimination
var shovelButton = new Text2('๐จ', {
size: 120,
fill: 0xFFFFFF
});
shovelButton.anchor.set(0.5, 0.5);
LK.gui.right.addChild(shovelButton);
// Create restart button on the left center
var restartGameButton = new Text2('๐', {
size: 120,
fill: 0xFFFFFF
});
restartGameButton.anchor.set(0.5, 0.5);
LK.gui.left.addChild(restartGameButton);
// Position tower buttons
basicTowerButton.y = -400;
splashTowerButton.y = -300;
slowTowerButton.y = -200;
sniperTowerButton.y = -100;
coinsText.y = 50;
livesText.y = 100;
var selectedTowerType = 'basic';
var shovelMode = false;
var isPaused = false;
var pauseMenu = null;
var pauseButton = null;
var restartButton = null;
var exitButton = null;
var musicButton = null;
var musicEnabled = true;
var gameOverMenu = null;
function createHomeScreen() {
if (homeScreen) return; // Already created
homeScreen = new Container();
// Create animated background
var background = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 32,
scaleY: 45
});
background.tint = 0x2c3e50;
background.alpha = 0.95;
background.x = 1024;
background.y = 1366;
homeScreen.addChild(background);
// Create game title with shadow effect
var shadowTitle = new Text2('TOWER DEFENSE', {
size: 160,
fill: 0x000000
});
shadowTitle.anchor.set(0.5, 0.5);
shadowTitle.x = 1029; // Slight offset for shadow
shadowTitle.y = 505;
homeScreen.addChild(shadowTitle);
var gameTitle = new Text2('TOWER DEFENSE', {
size: 160,
fill: 0xe74c3c
});
gameTitle.anchor.set(0.5, 0.5);
gameTitle.x = 1024;
gameTitle.y = 500;
homeScreen.addChild(gameTitle);
// Create subtitle
var subtitle = new Text2('Defend Your Path!', {
size: 80,
fill: 0x3498db
});
subtitle.anchor.set(0.5, 0.5);
subtitle.x = 1024;
subtitle.y = 650;
homeScreen.addChild(subtitle);
// Create start button
var startBg = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2
});
startBg.tint = 0x27ae60;
startBg.x = 1024;
startBg.y = 900;
homeScreen.addChild(startBg);
var startButton = new Text2('๐ฎ START GAME', {
size: 90,
fill: 0xffffff
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1024;
startButton.y = 900;
homeScreen.addChild(startButton);
// Create instructions
var instructionsTitle = new Text2('HOW TO PLAY:', {
size: 60,
fill: 0xf39c12
});
instructionsTitle.anchor.set(0.5, 0.5);
instructionsTitle.x = 1024;
instructionsTitle.y = 1200;
homeScreen.addChild(instructionsTitle);
var instruction1 = new Text2('โข Tap tower buttons to select type', {
size: 45,
fill: 0xffffff
});
instruction1.anchor.set(0.5, 0.5);
instruction1.x = 1024;
instruction1.y = 1300;
homeScreen.addChild(instruction1);
var instruction2 = new Text2('โข Tap empty grass to place towers', {
size: 45,
fill: 0xffffff
});
instruction2.anchor.set(0.5, 0.5);
instruction2.x = 1024;
instruction2.y = 1350;
homeScreen.addChild(instruction2);
var instruction3 = new Text2('โข Stop enemies from reaching the end!', {
size: 45,
fill: 0xffffff
});
instruction3.anchor.set(0.5, 0.5);
instruction3.x = 1024;
instruction3.y = 1400;
homeScreen.addChild(instruction3);
var instruction4 = new Text2('โข Use speed x2 button to fast forward', {
size: 45,
fill: 0xffffff
});
instruction4.anchor.set(0.5, 0.5);
instruction4.x = 1024;
instruction4.y = 1450;
homeScreen.addChild(instruction4);
// Create decorative tower icons
var basicIcon = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
basicIcon.x = 300;
basicIcon.y = 1600;
homeScreen.addChild(basicIcon);
var splashIcon = LK.getAsset('splashTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
splashIcon.x = 700;
splashIcon.y = 1600;
homeScreen.addChild(splashIcon);
var slowIcon = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
slowIcon.x = 1300;
slowIcon.y = 1600;
homeScreen.addChild(slowIcon);
var sniperIcon = LK.getAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
sniperIcon.x = 1700;
sniperIcon.y = 1600;
homeScreen.addChild(sniperIcon);
// Add floating animation to icons
tween(basicIcon, {
y: basicIcon.y + 20
}, {
duration: 2000,
loop: true,
yoyo: true
});
tween(splashIcon, {
y: splashIcon.y + 20
}, {
duration: 2200,
loop: true,
yoyo: true
});
tween(slowIcon, {
y: slowIcon.y + 20
}, {
duration: 2400,
loop: true,
yoyo: true
});
tween(sniperIcon, {
y: sniperIcon.y + 20
}, {
duration: 2600,
loop: true,
yoyo: true
});
// Add button event handler
startButton.down = function () {
tween(startButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
startGame();
}
});
};
// Animate title pulsing
function animateTitle() {
tween(gameTitle, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xff6b6b
}, {
duration: 2000,
onFinish: function onFinish() {
tween(gameTitle, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 2000,
onFinish: animateTitle
});
}
});
}
animateTitle();
// Animate start button
function animateStartButton() {
tween(startButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1500,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1500,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
game.addChild(homeScreen);
}
function startGame() {
gameState = 'playing';
if (homeScreen) {
homeScreen.destroy();
homeScreen = null;
}
// Start playing background music
LK.playMusic('bgmusic', {
volume: 1.0
});
}
function showCustomGameOver() {
if (gameOverMenu) return; // Already showing
isPaused = true;
// Create game over menu container
gameOverMenu = new Container();
// Create animated background with gradient effect
var background1 = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 32,
scaleY: 45
});
background1.tint = 0x1a1a2e;
background1.alpha = 0.95;
background1.x = 1024;
background1.y = 1366;
gameOverMenu.addChild(background1);
// Create decorative border elements
var borderTop = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 1
});
borderTop.tint = 0xe74c3c;
borderTop.x = 1024;
borderTop.y = 300;
gameOverMenu.addChild(borderTop);
var borderBottom = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 1
});
borderBottom.tint = 0xe74c3c;
borderBottom.x = 1024;
borderBottom.y = 2400;
gameOverMenu.addChild(borderBottom);
// Create large "GAME OVER" title with shadow effect
var shadowText = new Text2('GAME OVER', {
size: 140,
fill: 0x000000
});
shadowText.anchor.set(0.5, 0.5);
shadowText.x = 1029; // Slight offset for shadow
shadowText.y = 605;
gameOverMenu.addChild(shadowText);
var titleText = new Text2('GAME OVER', {
size: 140,
fill: 0xe74c3c
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
gameOverMenu.addChild(titleText);
// Create colorful stats display
var statsContainer = new Container();
// Stats background
var statsBg = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 8
});
statsBg.tint = 0x16213e;
statsBg.alpha = 0.8;
statsBg.x = 1024;
statsBg.y = 1000;
statsContainer.addChild(statsBg);
// Wave reached text
var waveReachedText = new Text2('Wave Reached: ' + wave, {
size: 60,
fill: 0x3498db
});
waveReachedText.anchor.set(0.5, 0.5);
waveReachedText.x = 1024;
waveReachedText.y = 850;
statsContainer.addChild(waveReachedText);
// Enemies killed text
var killsText = new Text2('Enemies Defeated: ' + enemiesKilled, {
size: 60,
fill: 0x2ecc71
});
killsText.anchor.set(0.5, 0.5);
killsText.x = 1024;
killsText.y = 950;
statsContainer.addChild(killsText);
// Final score text
var finalScore = wave * 100 + enemiesKilled * 10;
var scoreText = new Text2('Final Score: ' + finalScore, {
size: 60,
fill: 0xf39c12
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 1050;
statsContainer.addChild(scoreText);
// Best streak text
var streakText = new Text2('Best Streak: ' + killStreak, {
size: 60,
fill: 0x9b59b6
});
streakText.anchor.set(0.5, 0.5);
streakText.x = 1024;
streakText.y = 1150;
statsContainer.addChild(streakText);
gameOverMenu.addChild(statsContainer);
// Create animated restart button with glow effect
var restartBg = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
restartBg.tint = 0x27ae60;
restartBg.x = 1024;
restartBg.y = 1400;
gameOverMenu.addChild(restartBg);
var restartGameButton = new Text2('๐ PLAY AGAIN', {
size: 70,
fill: 0xffffff
});
restartGameButton.anchor.set(0.5, 0.5);
restartGameButton.x = 1024;
restartGameButton.y = 1400;
gameOverMenu.addChild(restartGameButton);
// Create animated exit button
var exitBg = LK.getAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
exitBg.tint = 0xc0392b;
exitBg.x = 1024;
exitBg.y = 1600;
gameOverMenu.addChild(exitBg);
var exitGameButton = new Text2('โ EXIT GAME', {
size: 70,
fill: 0xffffff
});
exitGameButton.anchor.set(0.5, 0.5);
exitGameButton.x = 1024;
exitGameButton.y = 1600;
gameOverMenu.addChild(exitGameButton);
// Add floating decorative elements
for (var i = 0; i < 8; i++) {
var decoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
decoration.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6][i % 5];
decoration.alpha = 0.6;
decoration.x = 200 + i * 225;
decoration.y = 400 + Math.sin(i) * 100;
gameOverMenu.addChild(decoration);
// Animate decorations
tween(decoration, {
y: decoration.y + 50,
rotation: Math.PI * 2
}, {
duration: 2000 + i * 200,
loop: true,
yoyo: true
});
}
// Add button event handlers
restartGameButton.down = function () {
tween(restartGameButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
LK.showGameOver();
}
});
};
exitGameButton.down = function () {
tween(exitGameButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0xe74c3c
}, {
duration: 100,
onFinish: function onFinish() {
LK.showGameOver();
}
});
};
// Animate title text with pulsing effect
function animateTitle() {
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xff6b6b
}, {
duration: 1000,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 1000,
onFinish: animateTitle
});
}
});
}
animateTitle();
// Animate stats container entrance
statsContainer.alpha = 0;
statsContainer.scaleX = 0.5;
statsContainer.scaleY = 0.5;
tween(statsContainer, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
delay: 500
});
game.addChild(gameOverMenu);
}
// Create pause button in top right corner
pauseButton = new Text2('โธ', {
size: 60,
fill: 0xFFFFFF
});
pauseButton.anchor.set(0, 0);
pauseButton.x = 150; // Offset from top right to avoid overlap with coins
pauseButton.y = 0;
LK.gui.topRight.addChild(pauseButton);
// Create speed x2 button
var speedButton = new Text2('x1', {
size: 50,
fill: 0x00FF00
});
speedButton.anchor.set(0.5, 0);
speedButton.x = 0; // Center horizontally
speedButton.y = 80; // Position below wave text
LK.gui.top.addChild(speedButton);
// Game speed state
var gameSpeed = 1;
function createPauseMenu() {
if (pauseMenu) return; // Already created
// Create semi-transparent background
pauseMenu = new Container();
var background = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 40
});
background.tint = 0x000000;
background.alpha = 0.7;
background.x = 1024;
background.y = 1366;
pauseMenu.addChild(background);
// Create menu title
var titleText = new Text2('PAUSED', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
pauseMenu.addChild(titleText);
// Create restart button
restartButton = new Text2('RESTART', {
size: 60,
fill: 0x00FF00
});
restartButton.anchor.set(0.5, 0.5);
restartButton.x = 1024;
restartButton.y = 1000;
pauseMenu.addChild(restartButton);
// Create exit button
exitButton = new Text2('EXIT', {
size: 60,
fill: 0xFF0000
});
exitButton.anchor.set(0.5, 0.5);
exitButton.x = 1024;
exitButton.y = 1200;
pauseMenu.addChild(exitButton);
// Create music toggle button
musicButton = new Text2(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF', {
size: 60,
fill: 0xFFFFFF
});
musicButton.tint = musicEnabled ? 0x00FFFF : 0x888888;
musicButton.anchor.set(0.5, 0.5);
musicButton.x = 1024;
musicButton.y = 1400;
pauseMenu.addChild(musicButton);
// Create resume button
var resumeButton = new Text2('RESUME', {
size: 60,
fill: 0xFFFF00
});
resumeButton.anchor.set(0.5, 0.5);
resumeButton.x = 1024;
resumeButton.y = 1600;
pauseMenu.addChild(resumeButton);
// Add event handlers
restartButton.down = function () {
LK.showGameOver(); // Restart the game
};
exitButton.down = function () {
showCustomGameOver(); // Exit to custom game over screen
};
musicButton.down = function () {
musicEnabled = !musicEnabled;
if (musicEnabled) {
LK.playMusic('bgmusic', {
volume: 1.0
});
musicButton.setText('MUSIC: ON');
musicButton.tint = 0x00FFFF;
} else {
LK.stopMusic();
musicButton.setText('MUSIC: OFF');
musicButton.tint = 0x888888;
}
};
resumeButton.down = function () {
togglePause();
};
game.addChild(pauseMenu);
}
function togglePause() {
isPaused = !isPaused;
if (isPaused) {
createPauseMenu();
pauseMenu.visible = true;
} else {
if (pauseMenu) {
pauseMenu.visible = false;
}
}
}
function showExitButton() {
isPaused = true;
if (!exitButton) {
// Create exit button if it doesn't exist
exitButton = new Text2('EXIT GAME', {
size: 60,
fill: 0xFF0000
});
exitButton.anchor.set(0.5, 0.5);
exitButton.x = 1024;
exitButton.y = 1366;
exitButton.down = function () {
showCustomGameOver(); // Exit to custom game over screen
};
game.addChild(exitButton);
} else {
exitButton.visible = true;
}
}
function updateUI() {
var coinDisplay = 'Coins: ' + coins;
coinsText.setText(coinDisplay);
livesText.setText('Lives: ' + lives);
waveText.setText('Wave: ' + wave);
if (showTowerMenu && selectedTower) {
// Show upgrade option
}
}
function getGridPosition(x, y) {
var gridX = Math.floor(x / gridSize);
var gridY = Math.floor(y / gridSize);
if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
return gameGrid[gridY][gridX];
}
return null;
}
function canPlaceTower(gridPos) {
return gridPos && !gridPos.occupied && !gridPos.isPath;
}
function spawnEnemy() {
var enemyType = 'basic';
var rand = Math.random();
// Every 15 waves is a golden enemy wave
if (wave % 15 === 0) {
enemyType = 'gold';
} else if (wave === 1) {
// Only basic enemies in wave 1
enemyType = 'basic';
} else {
// 5% chance for gold enemy (high reward, low health)
if (rand < 0.05) {
enemyType = 'gold';
} else if (wave >= 11) {
if (rand < 0.3) enemyType = 'fast';else if (rand < 0.6) enemyType = 'tank';
} else if (wave > 5) {
if (rand < 0.3) enemyType = 'fast';
} else if (wave >= 4) {
if (rand < 0.2) enemyType = 'fast';
}
}
var enemy = new Enemy(enemyType);
enemy.x = pathPoints[0].x;
enemy.y = pathPoints[0].y;
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function startWave() {
if (waveInProgress) return;
if (waveInProgress) return;
waveInProgress = true;
enemiesSpawned = 0;
enemiesPerWave = 10 + wave * 2;
spawnTimer = 0;
showTowerMenu = false;
selectedTower = null;
// Shovel mode can now be used continuously without resetting
// Shovel mode never wears out and persists throughout the game
// Display wave start message
showWaveStartMessage();
}
function showWaveStartMessage() {
// Create wave message container
var waveMessage = new Container();
waveMessage.x = 1024;
waveMessage.y = 1366;
// Create background for message
var messageBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 3
});
messageBg.tint = 0x2c3e50;
messageBg.alpha = 0.9;
waveMessage.addChild(messageBg);
// Create wave start text
var waveStartText = new Text2('WAVE ' + wave + ' STARTED!', {
size: 80,
fill: 0xf39c12
});
waveStartText.anchor.set(0.5, 0.5);
waveMessage.addChild(waveStartText);
// Create decorative elements
var leftDecoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
leftDecoration.tint = 0xe74c3c;
leftDecoration.x = -300;
waveMessage.addChild(leftDecoration);
var rightDecoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
rightDecoration.tint = 0x3498db;
rightDecoration.x = 300;
waveMessage.addChild(rightDecoration);
// Add to game
game.addChild(waveMessage);
// Initial state for animation
waveMessage.alpha = 0;
waveMessage.scaleX = 0.5;
waveMessage.scaleY = 0.5;
waveStartText.scaleX = 0.8;
waveStartText.scaleY = 0.8;
// Animate message entrance
tween(waveMessage, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Animate text pulsing
tween(waveStartText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waveStartText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
// Animate decorations spinning
tween(leftDecoration, {
rotation: Math.PI * 2
}, {
duration: 1000
});
tween(rightDecoration, {
rotation: -Math.PI * 2
}, {
duration: 1000
});
// Remove message after 2 seconds
LK.setTimeout(function () {
tween(waveMessage, {
alpha: 0,
scaleY: 0
}, {
duration: 400,
onFinish: function onFinish() {
waveMessage.destroy();
}
});
}, 2000);
}
function showWaveCompletionMessage() {
// Create wave completion message container
var completionMessage = new Container();
completionMessage.x = 1024;
completionMessage.y = 1366;
// Create background for message
var messageBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 14,
scaleY: 4
});
messageBg.tint = 0x27ae60;
messageBg.alpha = 0.9;
completionMessage.addChild(messageBg);
// Create wave completion text
var completionText = new Text2('WAVE ' + (wave - 1) + ' COMPLETED!', {
size: 70,
fill: 0xffffff
});
completionText.anchor.set(0.5, 0.5);
completionMessage.addChild(completionText);
// Create bonus text
var bonusText = new Text2('BONUS: +' + (50 + Math.floor(coins * 0.1)) + ' COINS', {
size: 50,
fill: 0xf1c40f
});
bonusText.anchor.set(0.5, 0.5);
bonusText.y = 60;
completionMessage.addChild(bonusText);
// Create decorative victory elements
var leftStar = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
leftStar.tint = 0xf39c12;
leftStar.x = -350;
completionMessage.addChild(leftStar);
var rightStar = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
rightStar.tint = 0x9b59b6;
rightStar.x = 350;
completionMessage.addChild(rightStar);
// Add sparkle effects
for (var i = 0; i < 6; i++) {
var sparkle = LK.getAsset('watercharc', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2
});
sparkle.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6, 0xe67e22][i];
sparkle.alpha = 0.8;
sparkle.x = -200 + i * 80;
sparkle.y = -80 + Math.sin(i) * 40;
completionMessage.addChild(sparkle);
// Animate sparkles with rotation and scaling
tween(sparkle, {
rotation: Math.PI * 2,
scaleX: 0.4,
scaleY: 0.4
}, {
duration: 1500,
easing: tween.easeInOut
});
}
// Add to game
game.addChild(completionMessage);
// Initial state for animation
completionMessage.alpha = 0;
completionMessage.scaleX = 0.3;
completionMessage.scaleY = 0.3;
completionText.scaleX = 0.5;
completionText.scaleY = 0.5;
// Animate message entrance with bounce effect
tween(completionMessage, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Animate main text with celebration effect
tween(completionText, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x2ecc71
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(completionText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 400
});
}
});
// Animate bonus text pulsing
tween(bonusText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bonusText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
// Animate stars spinning and scaling
tween(leftStar, {
rotation: Math.PI * 3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut
});
tween(rightStar, {
rotation: -Math.PI * 3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut
});
// Remove message after 2.5 seconds
LK.setTimeout(function () {
tween(completionMessage, {
alpha: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
completionMessage.destroy();
}
});
}, 2500);
}
// Event handlers
startWaveButton.down = function () {
// Add touch animation - scale down then back up
tween(startWaveButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(startWaveButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
startWave();
};
basicTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(basicTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00FF00
}, {
duration: 100,
onFinish: function onFinish() {
tween(basicTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'basic';
showTowerMenu = false;
};
splashTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(splashTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0xe74c3c
}, {
duration: 100,
onFinish: function onFinish() {
tween(splashTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'splash';
showTowerMenu = false;
};
slowTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(slowTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x9b59b6
}, {
duration: 100,
onFinish: function onFinish() {
tween(slowTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'slow';
showTowerMenu = false;
};
sniperTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(sniperTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
tween(sniperTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'sniper';
showTowerMenu = false;
};
pauseButton.down = function () {
// Add touch animation
tween(pauseButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(pauseButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
showExitButton();
};
speedButton.down = function () {
// Toggle speed between x1 and x2
gameSpeed = gameSpeed === 1 ? 2 : 1;
speedButton.setText('x' + gameSpeed);
speedButton.tint = gameSpeed === 2 ? 0xFF6600 : 0x00FF00;
// Add touch animation
tween(speedButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(speedButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150
});
}
});
};
shovelButton.down = function () {
// Toggle shovel mode
shovelMode = !shovelMode;
shovelButton.tint = shovelMode ? 0xFF0000 : 0xFFFFFF;
shovelButton.setText(shovelMode ? '๐จ' : '๐จ');
// Add touch animation
tween(shovelButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(shovelButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150
});
}
});
};
restartGameButton.down = function () {
// Add touch animation
tween(restartGameButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(restartGameButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
// Restart the game
LK.showGameOver();
};
game.down = function (x, y, obj) {
// Don't allow interactions during home screen
if (gameState === 'home') {
return;
}
if (showTowerMenu) {
showTowerMenu = false;
selectedTower = null;
return;
}
// Prevent interactions during wave
if (waveInProgress) {
return;
}
// Handle shovel mode - eliminate towers (infinite usage)
if (shovelMode) {
var gridPos = getGridPosition(x, y);
if (gridPos && gridPos.occupied) {
// Find and remove the tower at this position
for (var i = towers.length - 1; i >= 0; i--) {
var tower = towers[i];
var towerGridX = Math.floor(tower.x / gridSize);
var towerGridY = Math.floor(tower.y / gridSize);
var clickGridX = Math.floor(x / gridSize);
var clickGridY = Math.floor(y / gridSize);
if (towerGridX === clickGridX && towerGridY === clickGridY) {
// Give back half the tower cost
var refund = Math.floor(tower.cost * 0.5);
coins += refund;
// Mark grid position as unoccupied
gridPos.occupied = false;
// Remove tower from arrays and game
tower.destroy();
towers.splice(i, 1);
// Flash effect to show elimination
LK.effects.flashObject(tower, 0xFF0000, 200);
updateUI();
break;
}
}
}
return;
}
var gridPos = getGridPosition(x, y);
if (canPlaceTower(gridPos)) {
var tower = new Tower(selectedTowerType);
if (coins >= tower.cost) {
coins -= tower.cost;
tower.x = gridPos.x;
tower.y = gridPos.y;
gridPos.occupied = true;
towers.push(tower);
game.addChild(tower);
// Increase slow tower cost by 40 coins after placing one
if (selectedTowerType === 'slow') {
slowTowerCost += 40;
slowTowerButton.setText('Slow ($' + slowTowerCost + ')');
}
// Increase splash tower cost by 20 coins after placing one
if (selectedTowerType === 'splash') {
splashTowerCost += 20;
splashTowerButton.setText('Splash ($' + splashTowerCost + ')');
}
// Increase sniper tower cost by 100 coins after placing one
if (selectedTowerType === 'sniper') {
sniperTowerCost += 100;
sniperTowerButton.setText('Sniper ($' + sniperTowerCost + ')');
}
updateUI();
}
}
};
game.update = function () {
// Don't update game logic if on home screen
if (gameState === 'home') {
return;
}
// Don't update game logic if paused
if (isPaused) {
return;
}
// Animate coins text with rainbow colors
if (LK.ticks % 30 === 0) {
animateCoinsText();
}
// Spawn enemies during wave
if (waveInProgress && enemiesSpawned < enemiesPerWave) {
spawnTimer += gameSpeed;
if (spawnTimer >= 60) {
spawnEnemy();
spawnTimer = 0;
}
}
// Reset kill streak if lives were lost
if (lives < lastLives) {
killStreak = 0;
lastLives = lives;
}
// Check if wave is complete
if (waveInProgress && enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
waveInProgress = false;
wave++;
// Wave completion bonus + interest on current money
var waveBonus = 50;
var interestBonus = Math.floor(coins * 0.1); // 10% interest
coins += waveBonus + interestBonus;
// Display wave completion message
showWaveCompletionMessage();
updateUI();
}
// Clean up bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet.parent) {
bullets.splice(i, 1);
}
}
// Clean up water damage areas
for (var i = waterDamageAreas.length - 1; i >= 0; i--) {
var waterArea = waterDamageAreas[i];
if (!waterArea.parent) {
waterDamageAreas.splice(i, 1);
}
}
// Check win condition
if (wave > 20) {
LK.showYouWin();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (damage, target, assetType) {
var self = Container.call(this);
var bulletAsset = assetType || 'bullet';
var bulletGraphics = self.attachAsset(bulletAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = damage || 10;
self.target = target;
self.speed = 8;
self.update = function () {
if (!self.target || !self.target.parent) {
self.destroy();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.target.takeDamage(self.damage);
// If this is a slow bullet (iceball), apply slow effect
if (bulletAsset === 'iceball') {
self.target.slowed = true;
self.target.slowDuration = 240; // 4 seconds at 60fps
// Apply ice tint effect to show enemy is slowed
tween(self.target, {
tint: 0x87CEEB
}, {
duration: 200
});
}
// If this is a splash bullet, create persistent water damage area
if (self.isSplash) {
// Apply initial splash damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var splashDx = enemy.x - self.target.x;
var splashDy = enemy.y - self.target.y;
var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
if (splashDistance <= 80 && enemy !== self.target) {
enemy.takeDamage(Math.floor(self.damage * 0.5)); // Half damage for splash
}
}
// Only create persistent water damage area if flag is set (after 10 shots)
if (self.createWaterDamage) {
var waterDamage = new WaterDamage(self.target.x, self.target.y, Math.floor(self.damage * 0.3));
waterDamageAreas.push(waterDamage);
game.addChild(waterDamage);
}
}
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed * gameSpeed;
self.y += Math.sin(angle) * self.speed * gameSpeed;
// Add rainbow color cycling to bullets for colorful effect
var rainbowColors = [0xFF0000, 0xFF8C00, 0xFFD700, 0x00FF00, 0x00BFFF, 0x8A2BE2];
var colorIndex = Math.floor(LK.ticks / 10 % rainbowColors.length);
bulletGraphics.tint = rainbowColors[colorIndex];
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
self.enemyType = enemyType || 'basic';
self.pathIndex = 0;
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.reward = 10;
self.slowed = false;
self.slowDuration = 0;
var enemyGraphics;
if (self.enemyType === 'basic') {
enemyGraphics = self.attachAsset('basicEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 10 waves
var healthBonus = Math.floor(wave / 10) * 10; // +10 health per 10 waves
self.health = 30 + healthBonus;
self.maxHealth = 30 + healthBonus;
self.speed = 2;
self.reward = 10;
} else if (self.enemyType === 'fast') {
enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 11 waves
var healthBonus = Math.floor(wave / 11) * 20; // +20 health per 11 waves
self.health = 80 + healthBonus;
self.maxHealth = 80 + healthBonus;
self.speed = 4;
self.reward = 15;
} else if (self.enemyType === 'tank') {
enemyGraphics = self.attachAsset('tankEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 25 waves
var healthBonus = Math.floor(wave / 25) * 100; // +100 health per 25 waves
self.health = 400 + healthBonus;
self.maxHealth = 400 + healthBonus;
self.speed = 1;
self.reward = 25;
} else if (self.enemyType === 'gold') {
enemyGraphics = self.attachAsset('goldEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
self.speed = 3;
self.reward = 100; // High reward!
}
// Create health bar background
var healthBarBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1
});
healthBarBg.y = -enemyGraphics.height / 2 - 20;
healthBarBg.tint = 0x000000;
self.addChild(healthBarBg);
// Create health bar foreground
var healthBarFg = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1
});
healthBarFg.y = -enemyGraphics.height / 2 - 20;
healthBarFg.tint = 0x00FF00;
self.addChild(healthBarFg);
// Create health text to display current health as number
var healthText = new Text2(self.health.toString(), {
size: 30,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.y = -enemyGraphics.height / 2 - 20;
self.addChild(healthText);
self.healthBarBg = healthBarBg;
self.healthBarFg = healthBarFg;
self.healthText = healthText;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
self.healthBarFg.scaleX = 0.4 * healthPercent;
// Update health text to show current health number
self.healthText.setText(Math.max(0, self.health).toString());
// Change color based on health
if (healthPercent > 0.6) {
self.healthBarFg.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
self.healthBarFg.tint = 0xFFFF00; // Yellow
} else {
self.healthBarFg.tint = 0xFF0000; // Red
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
// Add colorful damage flash effect
var damageFlashColors = [0xFF4444, 0xFF8844, 0xFFFF44, 0x44FF44, 0x4444FF, 0xFF44FF];
var flashColor = damageFlashColors[Math.floor(Math.random() * damageFlashColors.length)];
tween(enemyGraphics, {
tint: flashColor,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 180
});
}
});
LK.getSound('enemyHit').play();
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Basic enemies give 2 coins, others use their reward value
var coinReward = self.enemyType === 'basic' ? 2 : self.reward;
coins += coinReward;
enemiesKilled++;
killStreak++;
LK.getSound('enemyDeath').play();
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
updateUI();
};
self.update = function () {
if (self.slowed) {
self.slowDuration--;
if (self.slowDuration <= 0) {
self.slowed = false;
// Remove ice tint when slow effect expires
tween(self, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
if (self.pathIndex >= pathPoints.length - 1) {
// Game over immediately when any enemy reaches the end
showCustomGameOver();
return;
}
var currentSpeed = self.slowed ? self.speed * 0.5 : self.speed;
currentSpeed *= gameSpeed; // Apply game speed multiplier
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * currentSpeed;
self.y += Math.sin(angle) * currentSpeed;
}
};
// Initialize health bar display
self.updateHealthBar();
return self;
});
var Tower = Container.expand(function (towerType) {
var self = Container.call(this);
self.towerType = towerType || 'basic';
self.level = 1;
self.range = 120;
self.damage = 10;
self.fireRate = 60;
self.cost = 50;
self.upgradeCost = 30;
self.lastShot = 0;
self.shotCount = 0; // Track number of shots for splash tower
var towerGraphics;
if (self.towerType === 'basic') {
towerGraphics = self.attachAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.range = 620;
self.fireRate = 60;
self.cost = 100;
} else if (self.towerType === 'splash') {
towerGraphics = self.attachAsset('splashTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.range = 600;
self.fireRate = 90;
self.cost = splashTowerCost;
} else if (self.towerType === 'slow') {
towerGraphics = self.attachAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.range = 640;
self.fireRate = 30;
self.cost = slowTowerCost;
} else if (self.towerType === 'sniper') {
towerGraphics = self.attachAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 100;
self.range = 1000;
self.fireRate = 300;
self.cost = sniperTowerCost;
}
self.canShoot = function () {
return LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed);
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
// Add colorful muzzle flash effect when shooting
var flashColors = {
'basic': 0xFFD700,
'splash': 0x00AAFF,
'slow': 0x87CEEB,
'sniper': 0xFF4500
};
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2,
tint: flashColors[self.towerType] || 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
if (self.towerType === 'basic') {
// Fire single bullet for basic tower
var bullet = new Bullet(self.damage, target);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'splash') {
// Splash tower fires waterball bullets and leaves watercharc damage
var bullet = new Bullet(self.damage, target, 'waterball');
bullet.x = self.x;
bullet.y = self.y;
bullet.isSplash = true; // Mark as splash bullet
self.shotCount++; // Increment shot counter
// Only create water damage after 10 shots
if (self.shotCount >= 10) {
bullet.createWaterDamage = true;
self.shotCount = 0; // Reset counter
}
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'slow') {
// Slow tower fires iceball bullets
var bullet = new Bullet(self.damage, target, 'iceball');
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'sniper') {
// Sniper tower fires sniperball bullets
var bullet = new Bullet(self.damage, target, 'sniperball');
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else {
// Other tower types fire single bullet
var bullet = new Bullet(self.damage, target);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
}
self.lastShot = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (coins >= self.upgradeCost && self.level < 3) {
coins -= self.upgradeCost;
self.level++;
self.damage = Math.floor(self.damage * 1.5);
self.range += 20;
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
updateUI();
}
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
self.down = function (x, y, obj) {
selectedTower = self;
showTowerMenu = true;
updateUI();
};
return self;
});
var WaterDamage = Container.expand(function (x, y, damage) {
var self = Container.call(this);
var waterGraphics = self.attachAsset('watercharc', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.damage = damage || 5; // Damage per tick
self.radius = 80; // Damage radius
self.lifetime = 300; // 5 seconds at 60fps
self.damageInterval = 30; // Damage every 0.5 seconds
self.lastDamageTick = 0;
waterGraphics.alpha = 0.7;
waterGraphics.tint = 0x00AAFF;
// Animate the water area appearance
waterGraphics.scaleX = 0.5;
waterGraphics.scaleY = 0.5;
tween(waterGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
self.update = function () {
self.lifetime--;
// Apply damage to enemies in range at intervals
if (LK.ticks - self.lastDamageTick >= self.damageInterval) {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.radius) {
enemy.takeDamage(self.damage);
}
}
self.lastDamageTick = LK.ticks;
}
// Fade out as lifetime decreases
if (self.lifetime < 60) {
// Start fading in last second
waterGraphics.alpha = 0.7 * (self.lifetime / 60);
}
// Remove when lifetime expires
if (self.lifetime <= 0) {
self.destroy();
// Remove from waterDamageAreas array
for (var j = waterDamageAreas.length - 1; j >= 0; j--) {
if (waterDamageAreas[j] === self) {
waterDamageAreas.splice(j, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8b4513 // Brown color
});
/****
* Game Code
****/
// Game state management
var gameState = 'home'; // 'home', 'playing'
var homeScreen = null;
var gridSize = 100;
var gridWidth = 20;
var gridHeight = 27;
var pathPoints = [{
x: 0,
y: 400
}, {
x: 300,
y: 400
}, {
x: 300,
y: 800
}, {
x: 700,
y: 800
}, {
x: 700,
y: 1200
}, {
x: 1100,
y: 1200
}, {
x: 1100,
y: 1600
}, {
x: 1500,
y: 1600
}, {
x: 1500,
y: 2000
}, {
x: 2048,
y: 2000
}];
var gameGrid = [];
var towers = [];
var enemies = [];
var bullets = [];
var waterDamageAreas = [];
var coins = 200;
var lives = 20;
var wave = 1;
var enemiesKilled = 0;
var killStreak = 0;
var lastLives = 20;
var enemiesSpawned = 0;
var enemiesPerWave = 10;
var spawnTimer = 0;
var waveInProgress = false;
var selectedTower = null;
var showTowerMenu = false;
var towerMenuButtons = [];
var slowTowerCost = 500; // Track dynamic slow tower cost
var splashTowerCost = 350; // Track dynamic splash tower cost
var sniperTowerCost = 1000; // Track dynamic sniper tower cost
// Create grid
for (var y = 0; y < gridHeight; y++) {
gameGrid[y] = [];
for (var x = 0; x < gridWidth; x++) {
gameGrid[y][x] = {
x: x * gridSize + 50,
y: y * gridSize + 50,
occupied: false,
isPath: false
};
}
}
// Mark path tiles
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var steps = Math.max(Math.abs(end.x - start.x), Math.abs(end.y - start.y)) / gridSize;
for (var step = 0; step <= steps; step++) {
var x = Math.floor((start.x + (end.x - start.x) * step / steps) / gridSize);
var y = Math.floor((start.y + (end.y - start.y) * step / steps) / gridSize);
if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) {
gameGrid[y][x].isPath = true;
}
}
}
// Create visual grid
for (var y = 0; y < gridHeight; y++) {
for (var x = 0; x < gridWidth; x++) {
var tile;
if (gameGrid[y][x].isPath) {
tile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
tile = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5
});
}
tile.x = gameGrid[y][x].x;
tile.y = gameGrid[y][x].y;
tile.alpha = 0.3;
game.addChild(tile);
}
}
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 60,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
LK.gui.topRight.addChild(coinsText);
// Add continuous rainbow animation to coins text
function animateCoinsText() {
var rainbowColors = [0xFFD700, 0xFF6B35, 0xF7931E, 0xFFD700, 0x00FF7F, 0x1E90FF, 0x9370DB];
var colorIndex = Math.floor(LK.ticks / 30 % rainbowColors.length);
tween(coinsText, {
tint: rainbowColors[colorIndex],
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
onFinish: function onFinish() {
tween(coinsText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
}
var livesText = new Text2('Lives: ' + lives, {
size: 50,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 0);
LK.gui.topRight.addChild(livesText);
var waveText = new Text2('Wave: ' + wave, {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var startWaveButton = new Text2('๐ START WAVE ๐', {
size: 80,
fill: 0x00FF00
});
startWaveButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(startWaveButton);
// Add colorful tween animation to make it more eye-catching
function animateStartButton() {
tween(startWaveButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onFinish() {
tween(startWaveButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
// Show home screen instead of starting immediately
createHomeScreen();
// Tower selection buttons
var basicTowerButton = new Text2('Basic ($100)', {
size: 80,
fill: 0xFFFFFF
});
basicTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(basicTowerButton);
var splashTowerButton = new Text2('Splash ($' + splashTowerCost + ')', {
size: 80,
fill: 0xFFFFFF
});
splashTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(splashTowerButton);
var slowTowerButton = new Text2('Slow ($' + slowTowerCost + ')', {
size: 80,
fill: 0xFFFFFF
});
slowTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(slowTowerButton);
var sniperTowerButton = new Text2('Sniper ($' + sniperTowerCost + ')', {
size: 80,
fill: 0xFFFFFF
});
sniperTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(sniperTowerButton);
// Create shovel button for tower elimination
var shovelButton = new Text2('๐จ', {
size: 120,
fill: 0xFFFFFF
});
shovelButton.anchor.set(0.5, 0.5);
LK.gui.right.addChild(shovelButton);
// Create restart button on the left center
var restartGameButton = new Text2('๐', {
size: 120,
fill: 0xFFFFFF
});
restartGameButton.anchor.set(0.5, 0.5);
LK.gui.left.addChild(restartGameButton);
// Position tower buttons
basicTowerButton.y = -400;
splashTowerButton.y = -300;
slowTowerButton.y = -200;
sniperTowerButton.y = -100;
coinsText.y = 50;
livesText.y = 100;
var selectedTowerType = 'basic';
var shovelMode = false;
var isPaused = false;
var pauseMenu = null;
var pauseButton = null;
var restartButton = null;
var exitButton = null;
var musicButton = null;
var musicEnabled = true;
var gameOverMenu = null;
function createHomeScreen() {
if (homeScreen) return; // Already created
homeScreen = new Container();
// Create animated background
var background = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 32,
scaleY: 45
});
background.tint = 0x2c3e50;
background.alpha = 0.95;
background.x = 1024;
background.y = 1366;
homeScreen.addChild(background);
// Create game title with shadow effect
var shadowTitle = new Text2('TOWER DEFENSE', {
size: 160,
fill: 0x000000
});
shadowTitle.anchor.set(0.5, 0.5);
shadowTitle.x = 1029; // Slight offset for shadow
shadowTitle.y = 505;
homeScreen.addChild(shadowTitle);
var gameTitle = new Text2('TOWER DEFENSE', {
size: 160,
fill: 0xe74c3c
});
gameTitle.anchor.set(0.5, 0.5);
gameTitle.x = 1024;
gameTitle.y = 500;
homeScreen.addChild(gameTitle);
// Create subtitle
var subtitle = new Text2('Defend Your Path!', {
size: 80,
fill: 0x3498db
});
subtitle.anchor.set(0.5, 0.5);
subtitle.x = 1024;
subtitle.y = 650;
homeScreen.addChild(subtitle);
// Create start button
var startBg = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2
});
startBg.tint = 0x27ae60;
startBg.x = 1024;
startBg.y = 900;
homeScreen.addChild(startBg);
var startButton = new Text2('๐ฎ START GAME', {
size: 90,
fill: 0xffffff
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1024;
startButton.y = 900;
homeScreen.addChild(startButton);
// Create instructions
var instructionsTitle = new Text2('HOW TO PLAY:', {
size: 60,
fill: 0xf39c12
});
instructionsTitle.anchor.set(0.5, 0.5);
instructionsTitle.x = 1024;
instructionsTitle.y = 1200;
homeScreen.addChild(instructionsTitle);
var instruction1 = new Text2('โข Tap tower buttons to select type', {
size: 45,
fill: 0xffffff
});
instruction1.anchor.set(0.5, 0.5);
instruction1.x = 1024;
instruction1.y = 1300;
homeScreen.addChild(instruction1);
var instruction2 = new Text2('โข Tap empty grass to place towers', {
size: 45,
fill: 0xffffff
});
instruction2.anchor.set(0.5, 0.5);
instruction2.x = 1024;
instruction2.y = 1350;
homeScreen.addChild(instruction2);
var instruction3 = new Text2('โข Stop enemies from reaching the end!', {
size: 45,
fill: 0xffffff
});
instruction3.anchor.set(0.5, 0.5);
instruction3.x = 1024;
instruction3.y = 1400;
homeScreen.addChild(instruction3);
var instruction4 = new Text2('โข Use speed x2 button to fast forward', {
size: 45,
fill: 0xffffff
});
instruction4.anchor.set(0.5, 0.5);
instruction4.x = 1024;
instruction4.y = 1450;
homeScreen.addChild(instruction4);
// Create decorative tower icons
var basicIcon = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
basicIcon.x = 300;
basicIcon.y = 1600;
homeScreen.addChild(basicIcon);
var splashIcon = LK.getAsset('splashTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
splashIcon.x = 700;
splashIcon.y = 1600;
homeScreen.addChild(splashIcon);
var slowIcon = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
slowIcon.x = 1300;
slowIcon.y = 1600;
homeScreen.addChild(slowIcon);
var sniperIcon = LK.getAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
sniperIcon.x = 1700;
sniperIcon.y = 1600;
homeScreen.addChild(sniperIcon);
// Add floating animation to icons
tween(basicIcon, {
y: basicIcon.y + 20
}, {
duration: 2000,
loop: true,
yoyo: true
});
tween(splashIcon, {
y: splashIcon.y + 20
}, {
duration: 2200,
loop: true,
yoyo: true
});
tween(slowIcon, {
y: slowIcon.y + 20
}, {
duration: 2400,
loop: true,
yoyo: true
});
tween(sniperIcon, {
y: sniperIcon.y + 20
}, {
duration: 2600,
loop: true,
yoyo: true
});
// Add button event handler
startButton.down = function () {
tween(startButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
startGame();
}
});
};
// Animate title pulsing
function animateTitle() {
tween(gameTitle, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xff6b6b
}, {
duration: 2000,
onFinish: function onFinish() {
tween(gameTitle, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 2000,
onFinish: animateTitle
});
}
});
}
animateTitle();
// Animate start button
function animateStartButton() {
tween(startButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1500,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1500,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
game.addChild(homeScreen);
}
function startGame() {
gameState = 'playing';
if (homeScreen) {
homeScreen.destroy();
homeScreen = null;
}
// Start playing background music
LK.playMusic('bgmusic', {
volume: 1.0
});
}
function showCustomGameOver() {
if (gameOverMenu) return; // Already showing
isPaused = true;
// Create game over menu container
gameOverMenu = new Container();
// Create animated background with gradient effect
var background1 = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 32,
scaleY: 45
});
background1.tint = 0x1a1a2e;
background1.alpha = 0.95;
background1.x = 1024;
background1.y = 1366;
gameOverMenu.addChild(background1);
// Create decorative border elements
var borderTop = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 1
});
borderTop.tint = 0xe74c3c;
borderTop.x = 1024;
borderTop.y = 300;
gameOverMenu.addChild(borderTop);
var borderBottom = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 1
});
borderBottom.tint = 0xe74c3c;
borderBottom.x = 1024;
borderBottom.y = 2400;
gameOverMenu.addChild(borderBottom);
// Create large "GAME OVER" title with shadow effect
var shadowText = new Text2('GAME OVER', {
size: 140,
fill: 0x000000
});
shadowText.anchor.set(0.5, 0.5);
shadowText.x = 1029; // Slight offset for shadow
shadowText.y = 605;
gameOverMenu.addChild(shadowText);
var titleText = new Text2('GAME OVER', {
size: 140,
fill: 0xe74c3c
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
gameOverMenu.addChild(titleText);
// Create colorful stats display
var statsContainer = new Container();
// Stats background
var statsBg = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 8
});
statsBg.tint = 0x16213e;
statsBg.alpha = 0.8;
statsBg.x = 1024;
statsBg.y = 1000;
statsContainer.addChild(statsBg);
// Wave reached text
var waveReachedText = new Text2('Wave Reached: ' + wave, {
size: 60,
fill: 0x3498db
});
waveReachedText.anchor.set(0.5, 0.5);
waveReachedText.x = 1024;
waveReachedText.y = 850;
statsContainer.addChild(waveReachedText);
// Enemies killed text
var killsText = new Text2('Enemies Defeated: ' + enemiesKilled, {
size: 60,
fill: 0x2ecc71
});
killsText.anchor.set(0.5, 0.5);
killsText.x = 1024;
killsText.y = 950;
statsContainer.addChild(killsText);
// Final score text
var finalScore = wave * 100 + enemiesKilled * 10;
var scoreText = new Text2('Final Score: ' + finalScore, {
size: 60,
fill: 0xf39c12
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 1050;
statsContainer.addChild(scoreText);
// Best streak text
var streakText = new Text2('Best Streak: ' + killStreak, {
size: 60,
fill: 0x9b59b6
});
streakText.anchor.set(0.5, 0.5);
streakText.x = 1024;
streakText.y = 1150;
statsContainer.addChild(streakText);
gameOverMenu.addChild(statsContainer);
// Create animated restart button with glow effect
var restartBg = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
restartBg.tint = 0x27ae60;
restartBg.x = 1024;
restartBg.y = 1400;
gameOverMenu.addChild(restartBg);
var restartGameButton = new Text2('๐ PLAY AGAIN', {
size: 70,
fill: 0xffffff
});
restartGameButton.anchor.set(0.5, 0.5);
restartGameButton.x = 1024;
restartGameButton.y = 1400;
gameOverMenu.addChild(restartGameButton);
// Create animated exit button
var exitBg = LK.getAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
exitBg.tint = 0xc0392b;
exitBg.x = 1024;
exitBg.y = 1600;
gameOverMenu.addChild(exitBg);
var exitGameButton = new Text2('โ EXIT GAME', {
size: 70,
fill: 0xffffff
});
exitGameButton.anchor.set(0.5, 0.5);
exitGameButton.x = 1024;
exitGameButton.y = 1600;
gameOverMenu.addChild(exitGameButton);
// Add floating decorative elements
for (var i = 0; i < 8; i++) {
var decoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
decoration.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6][i % 5];
decoration.alpha = 0.6;
decoration.x = 200 + i * 225;
decoration.y = 400 + Math.sin(i) * 100;
gameOverMenu.addChild(decoration);
// Animate decorations
tween(decoration, {
y: decoration.y + 50,
rotation: Math.PI * 2
}, {
duration: 2000 + i * 200,
loop: true,
yoyo: true
});
}
// Add button event handlers
restartGameButton.down = function () {
tween(restartGameButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
LK.showGameOver();
}
});
};
exitGameButton.down = function () {
tween(exitGameButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0xe74c3c
}, {
duration: 100,
onFinish: function onFinish() {
LK.showGameOver();
}
});
};
// Animate title text with pulsing effect
function animateTitle() {
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xff6b6b
}, {
duration: 1000,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 1000,
onFinish: animateTitle
});
}
});
}
animateTitle();
// Animate stats container entrance
statsContainer.alpha = 0;
statsContainer.scaleX = 0.5;
statsContainer.scaleY = 0.5;
tween(statsContainer, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
delay: 500
});
game.addChild(gameOverMenu);
}
// Create pause button in top right corner
pauseButton = new Text2('โธ', {
size: 60,
fill: 0xFFFFFF
});
pauseButton.anchor.set(0, 0);
pauseButton.x = 150; // Offset from top right to avoid overlap with coins
pauseButton.y = 0;
LK.gui.topRight.addChild(pauseButton);
// Create speed x2 button
var speedButton = new Text2('x1', {
size: 50,
fill: 0x00FF00
});
speedButton.anchor.set(0.5, 0);
speedButton.x = 0; // Center horizontally
speedButton.y = 80; // Position below wave text
LK.gui.top.addChild(speedButton);
// Game speed state
var gameSpeed = 1;
function createPauseMenu() {
if (pauseMenu) return; // Already created
// Create semi-transparent background
pauseMenu = new Container();
var background = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 40
});
background.tint = 0x000000;
background.alpha = 0.7;
background.x = 1024;
background.y = 1366;
pauseMenu.addChild(background);
// Create menu title
var titleText = new Text2('PAUSED', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
pauseMenu.addChild(titleText);
// Create restart button
restartButton = new Text2('RESTART', {
size: 60,
fill: 0x00FF00
});
restartButton.anchor.set(0.5, 0.5);
restartButton.x = 1024;
restartButton.y = 1000;
pauseMenu.addChild(restartButton);
// Create exit button
exitButton = new Text2('EXIT', {
size: 60,
fill: 0xFF0000
});
exitButton.anchor.set(0.5, 0.5);
exitButton.x = 1024;
exitButton.y = 1200;
pauseMenu.addChild(exitButton);
// Create music toggle button
musicButton = new Text2(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF', {
size: 60,
fill: 0xFFFFFF
});
musicButton.tint = musicEnabled ? 0x00FFFF : 0x888888;
musicButton.anchor.set(0.5, 0.5);
musicButton.x = 1024;
musicButton.y = 1400;
pauseMenu.addChild(musicButton);
// Create resume button
var resumeButton = new Text2('RESUME', {
size: 60,
fill: 0xFFFF00
});
resumeButton.anchor.set(0.5, 0.5);
resumeButton.x = 1024;
resumeButton.y = 1600;
pauseMenu.addChild(resumeButton);
// Add event handlers
restartButton.down = function () {
LK.showGameOver(); // Restart the game
};
exitButton.down = function () {
showCustomGameOver(); // Exit to custom game over screen
};
musicButton.down = function () {
musicEnabled = !musicEnabled;
if (musicEnabled) {
LK.playMusic('bgmusic', {
volume: 1.0
});
musicButton.setText('MUSIC: ON');
musicButton.tint = 0x00FFFF;
} else {
LK.stopMusic();
musicButton.setText('MUSIC: OFF');
musicButton.tint = 0x888888;
}
};
resumeButton.down = function () {
togglePause();
};
game.addChild(pauseMenu);
}
function togglePause() {
isPaused = !isPaused;
if (isPaused) {
createPauseMenu();
pauseMenu.visible = true;
} else {
if (pauseMenu) {
pauseMenu.visible = false;
}
}
}
function showExitButton() {
isPaused = true;
if (!exitButton) {
// Create exit button if it doesn't exist
exitButton = new Text2('EXIT GAME', {
size: 60,
fill: 0xFF0000
});
exitButton.anchor.set(0.5, 0.5);
exitButton.x = 1024;
exitButton.y = 1366;
exitButton.down = function () {
showCustomGameOver(); // Exit to custom game over screen
};
game.addChild(exitButton);
} else {
exitButton.visible = true;
}
}
function updateUI() {
var coinDisplay = 'Coins: ' + coins;
coinsText.setText(coinDisplay);
livesText.setText('Lives: ' + lives);
waveText.setText('Wave: ' + wave);
if (showTowerMenu && selectedTower) {
// Show upgrade option
}
}
function getGridPosition(x, y) {
var gridX = Math.floor(x / gridSize);
var gridY = Math.floor(y / gridSize);
if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
return gameGrid[gridY][gridX];
}
return null;
}
function canPlaceTower(gridPos) {
return gridPos && !gridPos.occupied && !gridPos.isPath;
}
function spawnEnemy() {
var enemyType = 'basic';
var rand = Math.random();
// Every 15 waves is a golden enemy wave
if (wave % 15 === 0) {
enemyType = 'gold';
} else if (wave === 1) {
// Only basic enemies in wave 1
enemyType = 'basic';
} else {
// 5% chance for gold enemy (high reward, low health)
if (rand < 0.05) {
enemyType = 'gold';
} else if (wave >= 11) {
if (rand < 0.3) enemyType = 'fast';else if (rand < 0.6) enemyType = 'tank';
} else if (wave > 5) {
if (rand < 0.3) enemyType = 'fast';
} else if (wave >= 4) {
if (rand < 0.2) enemyType = 'fast';
}
}
var enemy = new Enemy(enemyType);
enemy.x = pathPoints[0].x;
enemy.y = pathPoints[0].y;
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function startWave() {
if (waveInProgress) return;
if (waveInProgress) return;
waveInProgress = true;
enemiesSpawned = 0;
enemiesPerWave = 10 + wave * 2;
spawnTimer = 0;
showTowerMenu = false;
selectedTower = null;
// Shovel mode can now be used continuously without resetting
// Shovel mode never wears out and persists throughout the game
// Display wave start message
showWaveStartMessage();
}
function showWaveStartMessage() {
// Create wave message container
var waveMessage = new Container();
waveMessage.x = 1024;
waveMessage.y = 1366;
// Create background for message
var messageBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 3
});
messageBg.tint = 0x2c3e50;
messageBg.alpha = 0.9;
waveMessage.addChild(messageBg);
// Create wave start text
var waveStartText = new Text2('WAVE ' + wave + ' STARTED!', {
size: 80,
fill: 0xf39c12
});
waveStartText.anchor.set(0.5, 0.5);
waveMessage.addChild(waveStartText);
// Create decorative elements
var leftDecoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
leftDecoration.tint = 0xe74c3c;
leftDecoration.x = -300;
waveMessage.addChild(leftDecoration);
var rightDecoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
rightDecoration.tint = 0x3498db;
rightDecoration.x = 300;
waveMessage.addChild(rightDecoration);
// Add to game
game.addChild(waveMessage);
// Initial state for animation
waveMessage.alpha = 0;
waveMessage.scaleX = 0.5;
waveMessage.scaleY = 0.5;
waveStartText.scaleX = 0.8;
waveStartText.scaleY = 0.8;
// Animate message entrance
tween(waveMessage, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Animate text pulsing
tween(waveStartText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waveStartText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
// Animate decorations spinning
tween(leftDecoration, {
rotation: Math.PI * 2
}, {
duration: 1000
});
tween(rightDecoration, {
rotation: -Math.PI * 2
}, {
duration: 1000
});
// Remove message after 2 seconds
LK.setTimeout(function () {
tween(waveMessage, {
alpha: 0,
scaleY: 0
}, {
duration: 400,
onFinish: function onFinish() {
waveMessage.destroy();
}
});
}, 2000);
}
function showWaveCompletionMessage() {
// Create wave completion message container
var completionMessage = new Container();
completionMessage.x = 1024;
completionMessage.y = 1366;
// Create background for message
var messageBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 14,
scaleY: 4
});
messageBg.tint = 0x27ae60;
messageBg.alpha = 0.9;
completionMessage.addChild(messageBg);
// Create wave completion text
var completionText = new Text2('WAVE ' + (wave - 1) + ' COMPLETED!', {
size: 70,
fill: 0xffffff
});
completionText.anchor.set(0.5, 0.5);
completionMessage.addChild(completionText);
// Create bonus text
var bonusText = new Text2('BONUS: +' + (50 + Math.floor(coins * 0.1)) + ' COINS', {
size: 50,
fill: 0xf1c40f
});
bonusText.anchor.set(0.5, 0.5);
bonusText.y = 60;
completionMessage.addChild(bonusText);
// Create decorative victory elements
var leftStar = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
leftStar.tint = 0xf39c12;
leftStar.x = -350;
completionMessage.addChild(leftStar);
var rightStar = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
rightStar.tint = 0x9b59b6;
rightStar.x = 350;
completionMessage.addChild(rightStar);
// Add sparkle effects
for (var i = 0; i < 6; i++) {
var sparkle = LK.getAsset('watercharc', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2
});
sparkle.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6, 0xe67e22][i];
sparkle.alpha = 0.8;
sparkle.x = -200 + i * 80;
sparkle.y = -80 + Math.sin(i) * 40;
completionMessage.addChild(sparkle);
// Animate sparkles with rotation and scaling
tween(sparkle, {
rotation: Math.PI * 2,
scaleX: 0.4,
scaleY: 0.4
}, {
duration: 1500,
easing: tween.easeInOut
});
}
// Add to game
game.addChild(completionMessage);
// Initial state for animation
completionMessage.alpha = 0;
completionMessage.scaleX = 0.3;
completionMessage.scaleY = 0.3;
completionText.scaleX = 0.5;
completionText.scaleY = 0.5;
// Animate message entrance with bounce effect
tween(completionMessage, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Animate main text with celebration effect
tween(completionText, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x2ecc71
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(completionText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 400
});
}
});
// Animate bonus text pulsing
tween(bonusText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bonusText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
// Animate stars spinning and scaling
tween(leftStar, {
rotation: Math.PI * 3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut
});
tween(rightStar, {
rotation: -Math.PI * 3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut
});
// Remove message after 2.5 seconds
LK.setTimeout(function () {
tween(completionMessage, {
alpha: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
completionMessage.destroy();
}
});
}, 2500);
}
// Event handlers
startWaveButton.down = function () {
// Add touch animation - scale down then back up
tween(startWaveButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(startWaveButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
startWave();
};
basicTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(basicTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00FF00
}, {
duration: 100,
onFinish: function onFinish() {
tween(basicTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'basic';
showTowerMenu = false;
};
splashTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(splashTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0xe74c3c
}, {
duration: 100,
onFinish: function onFinish() {
tween(splashTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'splash';
showTowerMenu = false;
};
slowTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(slowTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x9b59b6
}, {
duration: 100,
onFinish: function onFinish() {
tween(slowTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'slow';
showTowerMenu = false;
};
sniperTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(sniperTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
tween(sniperTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'sniper';
showTowerMenu = false;
};
pauseButton.down = function () {
// Add touch animation
tween(pauseButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(pauseButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
showExitButton();
};
speedButton.down = function () {
// Toggle speed between x1 and x2
gameSpeed = gameSpeed === 1 ? 2 : 1;
speedButton.setText('x' + gameSpeed);
speedButton.tint = gameSpeed === 2 ? 0xFF6600 : 0x00FF00;
// Add touch animation
tween(speedButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(speedButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150
});
}
});
};
shovelButton.down = function () {
// Toggle shovel mode
shovelMode = !shovelMode;
shovelButton.tint = shovelMode ? 0xFF0000 : 0xFFFFFF;
shovelButton.setText(shovelMode ? '๐จ' : '๐จ');
// Add touch animation
tween(shovelButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(shovelButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150
});
}
});
};
restartGameButton.down = function () {
// Add touch animation
tween(restartGameButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(restartGameButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
// Restart the game
LK.showGameOver();
};
game.down = function (x, y, obj) {
// Don't allow interactions during home screen
if (gameState === 'home') {
return;
}
if (showTowerMenu) {
showTowerMenu = false;
selectedTower = null;
return;
}
// Prevent interactions during wave
if (waveInProgress) {
return;
}
// Handle shovel mode - eliminate towers (infinite usage)
if (shovelMode) {
var gridPos = getGridPosition(x, y);
if (gridPos && gridPos.occupied) {
// Find and remove the tower at this position
for (var i = towers.length - 1; i >= 0; i--) {
var tower = towers[i];
var towerGridX = Math.floor(tower.x / gridSize);
var towerGridY = Math.floor(tower.y / gridSize);
var clickGridX = Math.floor(x / gridSize);
var clickGridY = Math.floor(y / gridSize);
if (towerGridX === clickGridX && towerGridY === clickGridY) {
// Give back half the tower cost
var refund = Math.floor(tower.cost * 0.5);
coins += refund;
// Mark grid position as unoccupied
gridPos.occupied = false;
// Remove tower from arrays and game
tower.destroy();
towers.splice(i, 1);
// Flash effect to show elimination
LK.effects.flashObject(tower, 0xFF0000, 200);
updateUI();
break;
}
}
}
return;
}
var gridPos = getGridPosition(x, y);
if (canPlaceTower(gridPos)) {
var tower = new Tower(selectedTowerType);
if (coins >= tower.cost) {
coins -= tower.cost;
tower.x = gridPos.x;
tower.y = gridPos.y;
gridPos.occupied = true;
towers.push(tower);
game.addChild(tower);
// Increase slow tower cost by 40 coins after placing one
if (selectedTowerType === 'slow') {
slowTowerCost += 40;
slowTowerButton.setText('Slow ($' + slowTowerCost + ')');
}
// Increase splash tower cost by 20 coins after placing one
if (selectedTowerType === 'splash') {
splashTowerCost += 20;
splashTowerButton.setText('Splash ($' + splashTowerCost + ')');
}
// Increase sniper tower cost by 100 coins after placing one
if (selectedTowerType === 'sniper') {
sniperTowerCost += 100;
sniperTowerButton.setText('Sniper ($' + sniperTowerCost + ')');
}
updateUI();
}
}
};
game.update = function () {
// Don't update game logic if on home screen
if (gameState === 'home') {
return;
}
// Don't update game logic if paused
if (isPaused) {
return;
}
// Animate coins text with rainbow colors
if (LK.ticks % 30 === 0) {
animateCoinsText();
}
// Spawn enemies during wave
if (waveInProgress && enemiesSpawned < enemiesPerWave) {
spawnTimer += gameSpeed;
if (spawnTimer >= 60) {
spawnEnemy();
spawnTimer = 0;
}
}
// Reset kill streak if lives were lost
if (lives < lastLives) {
killStreak = 0;
lastLives = lives;
}
// Check if wave is complete
if (waveInProgress && enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
waveInProgress = false;
wave++;
// Wave completion bonus + interest on current money
var waveBonus = 50;
var interestBonus = Math.floor(coins * 0.1); // 10% interest
coins += waveBonus + interestBonus;
// Display wave completion message
showWaveCompletionMessage();
updateUI();
}
// Clean up bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet.parent) {
bullets.splice(i, 1);
}
}
// Clean up water damage areas
for (var i = waterDamageAreas.length - 1; i >= 0; i--) {
var waterArea = waterDamageAreas[i];
if (!waterArea.parent) {
waterDamageAreas.splice(i, 1);
}
}
// Check win condition
if (wave > 20) {
LK.showYouWin();
}
};
a tower of a tower defence. In-Game asset. 2d. High contrast. No shadows
slime. In-Game asset. 2d. High contrast. No shadows
splash tower of tower defense. In-Game asset. 2d. High contrast. No shadows
water bullet. In-Game asset. 2d. High contrast. No shadows
puddle of water. In-Game asset. 2d. High contrast. No shadows
slow tower of tower defense. In-Game asset. 2d. High contrast. No shadows
ice ball. In-Game asset. 2d. High contrast. No shadows
the sniper tower of tower defense. In-Game asset. 2d. High contrast. No shadows
sniper bullet. In-Game asset. 2d. High contrast. No shadows
floor. In-Game asset. 2d. High contrast. No shadows
fast enemy. In-Game asset. 2d. High contrast. No shadows
tank. In-Game asset. 2d. High contrast. No shadows