/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 250;
self.maxHealth = 250;
self.speed = 3.0 + Math.random() * 1.0; // Faster than other enemies
self.pathIndex = 0;
self.reward = 35; // Medium reward
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -55;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -55;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
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++;
if (self.pathIndex >= gamePath.length) {
// Bird reached the end - damage packet (8% of current health)
packetHealth -= Math.floor(packetHealth * 0.08);
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
birds.splice(birds.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction
if (dx > 0) {
birdGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
birdGraphics.scaleX = -1; // Face left
}
self.x += dx / distance * self.speed * gameSpeedMultiplier;
self.y += dy / distance * self.speed * gameSpeedMultiplier;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Bird defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
birds.splice(birds.indexOf(self), 1);
}
};
return self;
});
var Dog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('dog', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 400;
self.maxHealth = 400;
self.speed = 1.2 + Math.random() * 0.8; // Slower than most enemies
self.pathIndex = 0;
self.reward = 50; // High reward
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -70;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -70;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
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++;
if (self.pathIndex >= gamePath.length) {
// Dog reached the end - damage packet (20% of current health)
packetHealth -= Math.floor(packetHealth * 0.20);
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
dogs.splice(dogs.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction
if (dx > 0) {
dogGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
dogGraphics.scaleX = -1; // Face left
}
self.x += dx / distance * self.speed * gameSpeedMultiplier;
self.y += dy / distance * self.speed * gameSpeedMultiplier;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Dog defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
dogs.splice(dogs.indexOf(self), 1);
}
};
return self;
});
var Hare = Container.expand(function () {
var self = Container.call(this);
var hareGraphics = self.attachAsset('hare', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 300;
self.maxHealth = 300;
self.speed = 2.0 + Math.random() * 1.0; // Faster than rats
self.pathIndex = 0;
self.reward = 40; // Higher reward than rats
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -65;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -65;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
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++;
if (self.pathIndex >= gamePath.length) {
// Hare reached the end - damage packet (15% of original damage)
packetHealth -= Math.floor(packetHealth * 0.15); // 15% of current packet health
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
hares.splice(hares.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction like cats
if (dx > 0) {
hareGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
hareGraphics.scaleX = -1; // Face left
}
self.x += dx / distance * self.speed * gameSpeedMultiplier;
self.y += dy / distance * self.speed * gameSpeedMultiplier;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Hare defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
hares.splice(hares.indexOf(self), 1);
}
};
return self;
});
var Mouse = Container.expand(function () {
var self = Container.call(this);
var mouseGraphics = self.attachAsset('mouse', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 1.5 + Math.random() * 1.5; // Random speed between 1.5 and 3
self.pathIndex = 0;
self.reward = 10;
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
// Add random wandering to target position for more chaotic movement
var randomOffsetX = (Math.random() - 0.5) * 80;
var randomOffsetY = (Math.random() - 0.5) * 80;
var dx = target.x + randomOffsetX - self.x;
var dy = target.y + randomOffsetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15 + Math.random() * 10) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length) {
// Mouse reached the end - damage packet (5% of original damage)
packetHealth -= 5; // Changed to 5 damage (5% of original 100)
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
mice.splice(mice.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction like cats
if (dx > 0) {
mouseGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
mouseGraphics.scaleX = -1; // Face left
}
// Add slight random jitter to movement for more chaotic behavior
var jitterX = (Math.random() - 0.5) * 2;
var jitterY = (Math.random() - 0.5) * 2;
self.x += dx / distance * self.speed * gameSpeedMultiplier + jitterX;
self.y += dy / distance * self.speed * gameSpeedMultiplier + jitterY;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Mouse defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
mice.splice(mice.indexOf(self), 1);
}
};
return self;
});
var Rat = Container.expand(function () {
var self = Container.call(this);
var ratGraphics = self.attachAsset('rat', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.speed = 1.0 + Math.random() * 1.0; // Slower than before, similar to mice speed
self.pathIndex = 0;
self.reward = 25; // Higher reward
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -60;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -60;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
// Add random wandering behavior for more unpredictable movement
var randomOffsetX = (Math.random() - 0.5) * 100;
var randomOffsetY = (Math.random() - 0.5) * 100;
var dx = target.x + randomOffsetX - self.x;
var dy = target.y + randomOffsetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20 + Math.random() * 15) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length) {
// Rat reached the end - damage packet (10% of original damage)
packetHealth -= 10; // Changed to 10 damage (10% of original 100)
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
rats.splice(rats.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction like cats
if (dx > 0) {
ratGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
ratGraphics.scaleX = -1; // Face left
}
// Add random movement variation for more chaotic behavior
var jitterX = (Math.random() - 0.5) * 3;
var jitterY = (Math.random() - 0.5) * 3;
self.x += dx / distance * self.speed * gameSpeedMultiplier + jitterX;
self.y += dy / distance * self.speed * gameSpeedMultiplier + jitterY;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Rat defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
rats.splice(rats.indexOf(self), 1);
}
};
return self;
});
var Scratch = Container.expand(function (x, y) {
var self = Container.call(this);
self.attachAsset('scratch', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.rotation = Math.random() * Math.PI * 2;
self.alpha = 1;
var startScale = Math.random() * 0.5 + 0.8;
self.scale.set(startScale);
tween(self, {
alpha: 0,
scaleX: self.scale.x * 1.5,
scaleY: self.scale.y * 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.destroy();
}
}
});
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
var towerGraphics;
self.type = type || 'tired';
self.range = 450; // Increased base range for farther attacks
self.damage = 18; // Reduced from 25
self.fireRate = 30; // frames between shots (0.5 seconds at 60fps)
self.lastShot = 0;
if (self.type === 'tired') {
towerGraphics = self.attachAsset('tiredCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 20; // Increased from 19 to make game winnable
self.cost = 30;
self.fireRate = 60; // Slower attack rate for tired cats
self.range = 450; // Increased range for farther attacks
} else if (self.type === 'normal') {
towerGraphics = self.attachAsset('normalCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 55; // Increased from 45 to make normal cats even stronger
self.range = 500; // Increased range for farther attacks
self.cost = 150;
self.fireRate = 20; // Reduced from 30 to make normal cats faster
} else if (self.type === 'strong') {
towerGraphics = self.attachAsset('strongCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 35; // Reduced from 50
self.range = 550; // Increased range for farther attacks
self.cost = 150;
self.fireRate = 30;
} else if (self.type === 'strongest') {
towerGraphics = self.attachAsset('strongestCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 70; // Reduced from 100
self.range = 600; // Increased range for farther attacks
self.fireRate = 30;
self.cost = 250;
} else if (self.type === 'wild') {
towerGraphics = self.attachAsset('wildCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 50;
self.range = 580; // Increased range for farther attacks
self.fireRate = 20; // Faster than other cats
self.cost = 350;
} else if (self.type === 'tiger') {
towerGraphics = self.attachAsset('tiger', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 80;
self.range = 650; // Increased range for farther attacks
self.fireRate = 15; // Very fast attack rate
self.cost = 500;
}
self.update = function () {
// Don't allow towers being dragged to attack (prevents cheating)
if (isDragging && self === draggedTower) {
return;
}
self.lastShot += gameSpeedMultiplier;
var target = self.findTarget();
if (target) {
var currentFireRate = self.fireRate;
// Normal cat attacks rats extremely fast but mice slowly
if (self.type === 'normal') {
if (target.constructor.name === 'Rat') {
currentFireRate = 2; // Extremely fast against rats for superior effectiveness
} else {
currentFireRate = 60; // Slow attack rate against mice and other enemies
}
}
// Wild cat attacks hares and birds extremely fast but other enemies slowly
if (self.type === 'wild') {
if (target.constructor.name === 'Hare' || target.constructor.name === 'Bird') {
currentFireRate = 1; // Ultra fast against hares and birds for superior effectiveness
} else {
currentFireRate = 60; // Slow attack rate against mice and other enemies
}
}
// Tiger attacks dogs much faster than other enemies
if (self.type === 'tiger') {
if (target.constructor.name === 'Dog') {
currentFireRate = 5; // Much faster against dogs
} else {
currentFireRate = 20; // Normal fast rate against other enemies
}
}
if (self.lastShot >= currentFireRate) {
self.shoot(target);
self.lastShot = 0;
}
}
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
// Prioritize targets based on tower type
// Wild cats prioritize birds and hares
if (self.type === 'wild') {
// Check birds first
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
var distance = Math.sqrt(Math.pow(bird.x - self.x, 2) + Math.pow(bird.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = bird;
closestDistance = distance;
}
}
// Check hares second
for (var i = 0; i < hares.length; i++) {
var hare = hares[i];
var distance = Math.sqrt(Math.pow(hare.x - self.x, 2) + Math.pow(hare.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = hare;
closestDistance = distance;
}
}
} else if (self.type === 'tiger') {
// Tigers prioritize dogs
for (var i = 0; i < dogs.length; i++) {
var dog = dogs[i];
var distance = Math.sqrt(Math.pow(dog.x - self.x, 2) + Math.pow(dog.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = dog;
closestDistance = distance;
}
}
}
// Check all enemy types for remaining targeting
// Check mice
for (var i = 0; i < mice.length; i++) {
var mouse = mice[i];
var distance = Math.sqrt(Math.pow(mouse.x - self.x, 2) + Math.pow(mouse.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = mouse;
closestDistance = distance;
}
}
// Check rats
for (var i = 0; i < rats.length; i++) {
var rat = rats[i];
var distance = Math.sqrt(Math.pow(rat.x - self.x, 2) + Math.pow(rat.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = rat;
closestDistance = distance;
}
}
// Check hares
for (var i = 0; i < hares.length; i++) {
var hare = hares[i];
var distance = Math.sqrt(Math.pow(hare.x - self.x, 2) + Math.pow(hare.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = hare;
closestDistance = distance;
}
}
// Check birds
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
var distance = Math.sqrt(Math.pow(bird.x - self.x, 2) + Math.pow(bird.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = bird;
closestDistance = distance;
}
}
// Check dogs
for (var i = 0; i < dogs.length; i++) {
var dog = dogs[i];
var distance = Math.sqrt(Math.pow(dog.x - self.x, 2) + Math.pow(dog.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = dog;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
// Flip scaleX based on enemy position when shooting
var dx = target.x - self.x;
var dy = target.y - self.y;
// Flip horizontally based on enemy position (face the enemy directly)
var originalScaleX = 1;
if (target.x > self.x) {
originalScaleX = 1; // Face right toward enemy
towerGraphics.scaleX = 1;
} else if (target.x < self.x) {
originalScaleX = -1; // Face left toward enemy
towerGraphics.scaleX = -1;
}
// Calculate damage multiplier
var damageMultiplier = 1;
// Wild cats are excellent against birds and hares
if (self.type === 'wild' && (target.constructor.name === 'Hare' || target.constructor.name === 'Bird')) {
damageMultiplier = 2.0; // 100% bonus damage
// Predict target's next position for better accuracy
var predictedX = target.x + (dx > 0 ? target.speed * 3 : -target.speed * 3);
var predictedY = target.y + (dy > 0 ? target.speed * 3 : -target.speed * 3);
// Apply damage with slight delay for better visual effect
LK.setTimeout(function () {
if (target && target.parent) {
target.takeDamage(self.damage * damageMultiplier);
}
}, 50);
} else if (self.type === 'tiger' && target.constructor.name === 'Dog') {
// Tigers are extremely effective against dogs
damageMultiplier = 2.5; // 150% bonus damage
target.takeDamage(self.damage * damageMultiplier);
} else if (self.type === 'tiger') {
// Tigers attack other enemies well but not as good as dogs
damageMultiplier = 1.3; // 30% bonus damage for all other enemies
target.takeDamage(self.damage * damageMultiplier);
} else {
// Normal damage for other combinations
target.takeDamage(self.damage);
}
LK.getSound('shoot').play();
LK.getSound('hit').play();
// Create multiple scratches for visual effect
for (var i = 0; i < 3; i++) {
var scratchEffect = new Scratch(target.x + (Math.random() - 0.5) * 50, target.y + (Math.random() - 0.5) * 50);
game.addChild(scratchEffect);
}
// Smooth recoil animation - reduced intensity for tigers to prevent trembling
var recoilIntensity = self.type === 'tiger' ? 0.95 : 0.8;
var recoilDuration = self.type === 'tiger' ? 150 : 100;
tween(towerGraphics, {
scaleX: originalScaleX * recoilIntensity,
scaleY: recoilIntensity
}, {
duration: recoilDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: originalScaleX,
scaleY: 1
}, {
duration: recoilDuration,
easing: tween.easeInOut
});
}
});
};
self.getSellPrice = function () {
return Math.floor(self.cost * 0.7); // 70% of original cost
};
self.down = function (x, y, obj) {
if (!gameStarted && !isDragging) {
// Allow repositioning before game starts
if (selectedTower) {
selectedTower.tint = 0xffffff; // Reset previous selection tint
}
selectedTower = self;
self.tint = 0x00ffff; // Cyan tint to show selection
isDragging = true;
draggedTower = self;
// Play pickup sound for repositioning
LK.getSound('pickupDefender').play();
// No need to free placement spots since we're using free positioning
} else if (!isDragging) {
showSellUI(self);
}
};
return self;
});
/****
* Initialize Game
****/
// Game state variables
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Add background image
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
background.x = 1024; // Center horizontally (2048/2)
background.y = 1366; // Center vertically (2732/2)
// Game assets
// Import tween plugin for animations
// Game state variables
var mice = [];
var rats = [];
var hares = [];
var birds = [];
var dogs = [];
var towers = [];
var gameGold = 120;
var packetHealth = 100;
var waveNumber = 1;
var miceSpawned = 0;
var ratsSpawned = 0;
var haresSpawned = 0;
var birdsSpawned = 0;
var dogsSpawned = 0;
var maxMicePerWave = 15;
var maxRatsPerWave = 0;
var maxHaresPerWave = 0;
var maxBirdsPerWave = 0;
var maxDogsPerWave = 0;
var spawnTimer = 0;
var ratSpawnTimer = 0;
var hareSpawnTimer = 0;
var birdSpawnTimer = 0;
var dogSpawnTimer = 0;
var spawnRate = 90; // frames between spawns (much faster spawning for harder waves)
var ratSpawnRate = 100; // frames between rat spawns (faster)
var hareSpawnRate = 120; // frames between hare spawns
var birdSpawnRate = 80; // frames between bird spawns (faster due to speed)
var dogSpawnRate = 140; // frames between dog spawns (slower)
var gameStarted = false;
var gameStartTimer = 0;
var isPaused = false;
var pauseTimer = 0;
var pauseText = null;
var gameSpeedMultiplier = 1; // Normal speed = 1, Fast speed = 2
// Create path for mice - random zigzag movement with more randomization
var gamePath = [{
x: 1024 + (Math.random() - 0.5) * 200,
y: 50 + Math.random() * 100
}, {
x: 600 + (Math.random() - 0.5) * 400,
y: 300 + (Math.random() - 0.5) * 200
}, {
x: 1400 + (Math.random() - 0.5) * 400,
y: 600 + (Math.random() - 0.5) * 200
}, {
x: 400 + (Math.random() - 0.5) * 300,
y: 900 + (Math.random() - 0.5) * 200
}, {
x: 1600 + (Math.random() - 0.5) * 300,
y: 1200 + (Math.random() - 0.5) * 200
}, {
x: 800 + (Math.random() - 0.5) * 400,
y: 1500 + (Math.random() - 0.5) * 200
}, {
x: 1200 + (Math.random() - 0.5) * 400,
y: 1800 + (Math.random() - 0.5) * 200
}, {
x: 500 + (Math.random() - 0.5) * 300,
y: 2100 + (Math.random() - 0.5) * 200
}, {
x: 1500 + (Math.random() - 0.5) * 300,
y: 2400 + (Math.random() - 0.5) * 200
}, {
x: 1024 + (Math.random() - 0.5) * 200,
y: 2700 + Math.random() * 100
}];
// Draw path
for (var i = 0; i < gamePath.length; i++) {
var pathSegment = game.addChild(LK.getAsset('path', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
}));
pathSegment.x = gamePath[i].x;
pathSegment.y = gamePath[i].y;
}
// Create packet of treats at the end
var packet = game.addChild(LK.getAsset('packetTreats', {
anchorX: 0.5,
anchorY: 0.5
}));
packet.x = gamePath[gamePath.length - 1].x;
packet.y = gamePath[gamePath.length - 1].y - 200;
// Create placement system - cats can be placed anywhere near the path
var placementSpots = []; // Keep for compatibility but will be used differently
var maxDistanceFromPath = 300; // Maximum distance cats can be placed from path
// Helper function to check if a position is valid for tower placement
function isValidPlacementPosition(x, y) {
// Check if position is within game bounds
if (x < 100 || x > 1948 || y < 100 || y > 2632) {
return false;
}
// Check if position is too close to existing towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var dx = tower.x - x;
var dy = tower.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 120) {
// Minimum distance between towers
return false;
}
}
return true;
}
// UI Elements
var goldText = new Text2(' Gold: ' + gameGold, {
size: 60,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
goldText.anchor.set(0, 0);
goldText.x = 50;
goldText.y = 50;
LK.gui.topLeft.addChild(goldText);
var healthText = new Text2('Packet Health: ' + packetHealth, {
size: 60,
fill: 0xFF0000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
healthText.anchor.set(0.5, 0);
healthText.x = 0;
healthText.y = 50;
LK.gui.top.addChild(healthText);
var waveText = new Text2('Wave: ' + waveNumber, {
size: 60,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
waveText.anchor.set(1, 0);
waveText.x = 0;
waveText.y = 50;
LK.gui.topRight.addChild(waveText);
// Create start button square
var startButton = LK.getAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3,
alpha: 0
});
startButton.x = 0;
startButton.y = 0;
startButton.interactive = false;
LK.gui.center.addChild(startButton);
// Add tutorial images in front of start button
var tutorialCat = LK.getAsset('tiredCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
});
tutorialCat.x = -120;
tutorialCat.y = -20;
LK.gui.center.addChild(tutorialCat);
// Add arrow image pointing from cat to mouse
var tutorialArrow = new Text2('→', {
size: 180,
fill: 0xFFFF00,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
tutorialArrow.anchor.set(0.5, 0.5);
tutorialArrow.x = 0;
tutorialArrow.y = -20;
tutorialArrow.alpha = 0;
LK.gui.center.addChild(tutorialArrow);
// Add mouse image
var tutorialMouse = LK.getAsset('mouse', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0
});
tutorialMouse.x = 120;
tutorialMouse.y = -20;
LK.gui.center.addChild(tutorialMouse);
// Add wave 5 waiting state
var waitingForWave5Start = false;
// Add click handler to start button
startButton.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
// Play start sound
LK.getSound('start').play();
// Smoothly fade out start button and tutorial elements together
tween(startButton, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
startButton.interactive = false;
// Smoothly fade out tutorial images at the same time
tween(tutorialCat, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialArrow, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialMouse, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
} else if (waitingForWave5Start) {
// Wave 5 start button clicked
waitingForWave5Start = false;
isPaused = false;
pauseTimer = 0;
// Play start sound
LK.getSound('start').play();
// Smoothly fade out start button and tutorial elements together
tween(startButton, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
startButton.interactive = false;
// Smoothly fade out tutorial images at the same time
tween(tutorialCat, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialArrow, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialMouse, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
};
// Show start button after 10 seconds
LK.setTimeout(function () {
if (!gameStarted) {
tween(startButton, {
alpha: 1
}, {
duration: 500
});
// Show tutorial images
tween(tutorialCat, {
alpha: 1
}, {
duration: 500
});
tween(tutorialArrow, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
// Add pulsing effect to make arrow more visible
tween(tutorialArrow, {
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tutorialArrow, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
}
});
tween(tutorialMouse, {
alpha: 1
}, {
duration: 500
});
startButton.interactive = true;
}
}, 10000);
// Tower selection UI
var selectedTowerType = 'normal';
// Create bottom menu background
var menuBackground = LK.getAsset('placementGrid', {
anchorX: 0.5,
anchorY: 1,
scaleX: 20,
scaleY: 2,
alpha: 0.8
});
menuBackground.x = 0;
menuBackground.y = 0;
LK.gui.bottom.addChild(menuBackground);
// Create tired cat button with asset
var tiredCatButton = LK.getAsset('tiredCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
tiredCatButton.x = -200;
tiredCatButton.y = -80;
LK.gui.bottom.addChild(tiredCatButton);
// Create price text for tired cat
var tiredCatPrice = new Text2('30g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
tiredCatPrice.anchor.set(0.5, 0);
tiredCatPrice.x = -200;
tiredCatPrice.y = -40;
LK.gui.bottom.addChild(tiredCatPrice);
// Create normal cat button with asset
var normalCatButton = LK.getAsset('normalCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
normalCatButton.x = 200;
normalCatButton.y = -80;
LK.gui.bottom.addChild(normalCatButton);
// Create price text for normal cat
var normalCatPrice = new Text2('150g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
normalCatPrice.anchor.set(0.5, 0);
normalCatPrice.x = 200;
normalCatPrice.y = -40;
LK.gui.bottom.addChild(normalCatPrice);
// Create wild cat button with asset
var wildCatButton = LK.getAsset('wildCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
wildCatButton.x = 400;
wildCatButton.y = -80;
LK.gui.bottom.addChild(wildCatButton);
// Create price text for wild cat
var wildCatPrice = new Text2('350g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
wildCatPrice.anchor.set(0.5, 0);
wildCatPrice.x = 400;
wildCatPrice.y = -40;
LK.gui.bottom.addChild(wildCatPrice);
// Create tiger button with asset
var tigerButton = LK.getAsset('tiger', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
tigerButton.x = 600;
tigerButton.y = -80;
LK.gui.bottom.addChild(tigerButton);
// Create price text for tiger
var tigerPrice = new Text2('500g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
tigerPrice.anchor.set(0.5, 0);
tigerPrice.x = 600;
tigerPrice.y = -40;
LK.gui.bottom.addChild(tigerPrice);
// Create speed control buttons
var fastForwardButton = new Text2('⏩', {
size: 80,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
fastForwardButton.anchor.set(1, 0);
fastForwardButton.x = -20;
fastForwardButton.y = 120;
LK.gui.topRight.addChild(fastForwardButton);
var normalSpeedButton = new Text2('▶️', {
size: 80,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
normalSpeedButton.anchor.set(1, 0);
normalSpeedButton.x = -20;
normalSpeedButton.y = 220;
fastForwardButton.alpha = 1; // Start opaque since fast speed is not selected
normalSpeedButton.alpha = 0.5; // Start transparent since normal speed is selected
LK.gui.topRight.addChild(normalSpeedButton);
// Initialize button transparency
updateTiredCatButtonAlpha();
updateNormalCatButtonAlpha();
updateWildCatButtonAlpha();
updateTigerButtonAlpha();
// Speed control button handlers
fastForwardButton.down = function (x, y, obj) {
gameSpeedMultiplier = 2;
// Make fast button transparent and normal button opaque (clicked option is transparent)
tween(fastForwardButton, {
alpha: 0.5
}, {
duration: 200
});
tween(normalSpeedButton, {
alpha: 1
}, {
duration: 200
});
};
normalSpeedButton.down = function (x, y, obj) {
gameSpeedMultiplier = 1;
// Make normal button transparent and fast button opaque (clicked option is transparent)
tween(fastForwardButton, {
alpha: 1
}, {
duration: 200
});
tween(normalSpeedButton, {
alpha: 0.5
}, {
duration: 200
});
};
// Dragging variables
var isDragging = false;
var draggedTower = null;
var dragOffset = {
x: 0,
y: 0
};
// Sell UI variables
var sellUI = null;
var sellUITower = null;
// Tower selection for repositioning
var selectedTower = null;
function updateGoldDisplay() {
goldText.setText(' Gold: ' + gameGold);
updateTiredCatButtonAlpha();
updateNormalCatButtonAlpha();
updateWildCatButtonAlpha();
updateTigerButtonAlpha();
}
function updatePacketDisplay() {
healthText.setText('Packet Health: ' + packetHealth);
}
function updateWaveDisplay() {
waveText.setText('Wave: ' + waveNumber);
}
function showSellUI(tower) {
// Remove existing sell UI
if (sellUI) {
sellUI.destroy();
sellUI = null;
}
sellUITower = tower;
sellUI = new Container();
game.addChild(sellUI);
// Position sell UI above tower
sellUI.x = tower.x;
sellUI.y = tower.y - 100;
// Create sell button
var sellButton = sellUI.attachAsset('sellButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Create sell price text
var sellPrice = tower.getSellPrice();
var sellText = new Text2('SELL\n' + sellPrice + 'g', {
size: 24,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
sellText.anchor.set(0.5, 0.5);
sellText.x = 0;
sellText.y = 0;
sellUI.addChild(sellText);
// Sell button click handler
sellButton.down = function (x, y, obj) {
sellTower(sellUITower);
};
// Auto-hide sell UI after 3 seconds
LK.setTimeout(function () {
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
}, 3000);
}
function sellTower(tower) {
// Give back gold (70% of original cost)
var sellPrice = tower.getSellPrice();
gameGold += sellPrice;
updateGoldDisplay();
LK.getSound('mouseHit').play();
// No need to free placement spots since we're using free positioning
// Remove tower from towers array
var towerIndex = towers.indexOf(tower);
if (towerIndex > -1) {
towers.splice(towerIndex, 1);
}
// Destroy tower
tower.destroy();
// Hide sell UI
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
}
// Tired cat button drag functionality
tiredCatButton.down = function (x, y, obj) {
if (gameGold >= 30) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'tired';
// Create dragged tower preview
draggedTower = new Tower('tired');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(tiredCatButton.parent.toGlobal(tiredCatButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(tiredCatButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(tiredCatButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
}
});
}
};
function updateTiredCatButtonAlpha() {
if (gameGold >= 30) {
tiredCatButton.alpha = 1;
tiredCatPrice.alpha = 1;
} else {
tiredCatButton.alpha = 0.5;
tiredCatPrice.alpha = 0.5;
}
}
// Add move handler to tired cat button for drag support
tiredCatButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'tired') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(tiredCatButton.parent.toGlobal(tiredCatButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Normal cat button drag functionality
normalCatButton.down = function (x, y, obj) {
if (gameGold >= 150) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'normal';
// Create dragged tower preview
draggedTower = new Tower('normal');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(normalCatButton.parent.toGlobal(normalCatButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(normalCatButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100,
onFinish: function onFinish() {
tween(normalCatButton, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 100
});
}
});
}
};
function updateNormalCatButtonAlpha() {
if (gameGold >= 150) {
normalCatButton.alpha = 1;
normalCatPrice.alpha = 1;
} else {
normalCatButton.alpha = 0.5;
normalCatPrice.alpha = 0.5;
}
}
// Add move handler to normal cat button for drag support
normalCatButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'normal') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(normalCatButton.parent.toGlobal(normalCatButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Wild cat button drag functionality
wildCatButton.down = function (x, y, obj) {
if (gameGold >= 350) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'wild';
// Create dragged tower preview
draggedTower = new Tower('wild');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(wildCatButton.parent.toGlobal(wildCatButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(wildCatButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(wildCatButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100
});
}
});
}
};
function updateWildCatButtonAlpha() {
if (gameGold >= 350) {
wildCatButton.alpha = 1;
wildCatPrice.alpha = 1;
} else {
wildCatButton.alpha = 0.5;
wildCatPrice.alpha = 0.5;
}
}
// Add move handler to wild cat button for drag support
wildCatButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'wild') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(wildCatButton.parent.toGlobal(wildCatButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Tiger button drag functionality
tigerButton.down = function (x, y, obj) {
if (gameGold >= 500) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'tiger';
// Create dragged tower preview
draggedTower = new Tower('tiger');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(tigerButton.parent.toGlobal(tigerButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(tigerButton, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 100,
onFinish: function onFinish() {
tween(tigerButton, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 100
});
}
});
}
};
function updateTigerButtonAlpha() {
if (gameGold >= 500) {
tigerButton.alpha = 1;
tigerPrice.alpha = 1;
} else {
tigerButton.alpha = 0.5;
tigerPrice.alpha = 0.5;
}
}
// Add move handler to tiger button for drag support
tigerButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'tiger') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(tigerButton.parent.toGlobal(tigerButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Add up handlers to all tower buttons
tiredCatButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
normalCatButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
wildCatButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
tigerButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
// Game move handler for dragging
game.move = function (x, y, obj) {
if (isDragging && draggedTower) {
draggedTower.x = x;
draggedTower.y = y;
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(x, y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else if (selectedTower === draggedTower) {
draggedTower.tint = 0x00ffff; // Keep cyan tint for selected tower being repositioned
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Game up handler for tower placement
game.up = function (x, y, obj) {
if (isDragging && draggedTower) {
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(x, y);
// Check if this is a repositioning of an existing tower
var isRepositioning = selectedTower === draggedTower;
var towerCost = isRepositioning ? 0 : draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place or reposition tower at current position
if (!isRepositioning) {
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
}
// Keep tower at current dragged position
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
if (!isRepositioning) {
LK.getSound('placeDefender').play();
}
} else if (isRepositioning) {
// If repositioning failed, keep tower at its current position but remove selection
draggedTower.tint = 0xffffff;
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging and selection state
isDragging = false;
draggedTower = null;
if (selectedTower) {
selectedTower = null;
}
}
};
// Start background music
LK.playMusic('bgMusic');
game.update = function () {
// Don't spawn mice until game starts (start button clicked)
if (!gameStarted) {
return; // Don't spawn mice until game starts
}
// Handle pause after wave 4 (changed from wave 3) or waiting for wave 5 start
if (isPaused || waitingForWave5Start) {
if (isPaused) {
pauseTimer += gameSpeedMultiplier;
if (pauseTimer >= 600) {
// 10 seconds at 60fps
isPaused = false;
// Remove pause text
if (pauseText) {
pauseText.destroy();
pauseText = null;
}
// Show start button with normal cat, arrow, rat after wave 4
if (waveNumber === 5) {
// Wave number increments before pause check
// Set waiting state for wave 5
waitingForWave5Start = true;
// Show start button
startButton.alpha = 1;
startButton.interactive = true;
// Change tutorial images for wave 4 completion
tutorialCat.destroy();
tutorialCat = LK.getAsset('normalCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 1
});
tutorialCat.x = -120;
tutorialCat.y = -20;
LK.gui.center.addChild(tutorialCat);
tutorialArrow.alpha = 1;
// Change mouse to rat
tutorialMouse.destroy();
tutorialMouse = LK.getAsset('rat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 1
});
tutorialMouse.x = 120;
tutorialMouse.y = -20;
LK.gui.center.addChild(tutorialMouse);
}
}
}
return; // Don't spawn enemies during pause or while waiting for wave 5 start
}
// Spawn mice with much more random timing variation
spawnTimer += gameSpeedMultiplier;
var randomSpawnDelay = Math.random() * 60 + Math.random() * 30; // Much more random delay
if (spawnTimer >= spawnRate + randomSpawnDelay && miceSpawned < maxMicePerWave) {
var mouse = new Mouse();
// Much more random spawn positions with larger variation
mouse.x = gamePath[0].x + (Math.random() - 0.5) * 600 + Math.random() * 200;
mouse.y = gamePath[0].y + (Math.random() - 0.5) * 400 + Math.random() * 150;
// More random health variation (±40%)
var baseHealth = 180 + waveNumber * 50;
mouse.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.8;
mouse.maxHealth = mouse.health;
// Much more random speed variation (±50%)
mouse.speed = mouse.speed * (0.5 + Math.random() * 1.0);
mouse.updateHealthBar();
mice.push(mouse);
game.addChild(mouse);
miceSpawned++;
spawnTimer = Math.random() * 20; // Random reset for more chaos
}
// Spawn rats (only after wave 5) with random variations
if (waveNumber >= 5) {
ratSpawnTimer += gameSpeedMultiplier;
var randomRatDelay = Math.random() * 40;
if (ratSpawnTimer >= ratSpawnRate + randomRatDelay && ratsSpawned < maxRatsPerWave) {
var rat = new Rat();
rat.x = gamePath[0].x + (Math.random() - 0.5) * 500;
rat.y = gamePath[0].y + (Math.random() - 0.5) * 250;
// Random health variation (±25%)
var baseHealth = 500 + waveNumber * 120;
rat.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.5;
rat.maxHealth = rat.health;
// Random speed variation (±40%)
rat.speed = rat.speed * (0.6 + Math.random() * 0.8);
rat.updateHealthBar();
rats.push(rat);
game.addChild(rat);
ratsSpawned++;
ratSpawnTimer = 0;
}
}
// Spawn hares (only after wave 10) with random variations
if (waveNumber >= 10) {
hareSpawnTimer += gameSpeedMultiplier;
var randomHareDelay = Math.random() * 50;
if (hareSpawnTimer >= hareSpawnRate + randomHareDelay && haresSpawned < maxHaresPerWave) {
var hare = new Hare();
hare.x = gamePath[0].x + (Math.random() - 0.5) * 600;
hare.y = gamePath[0].y + (Math.random() - 0.5) * 300;
// Random health variation (±30%)
var baseHealth = 700 + waveNumber * 150;
hare.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.6;
hare.maxHealth = hare.health;
// Random speed variation (±35%)
hare.speed = hare.speed * (0.65 + Math.random() * 0.7);
hare.updateHealthBar();
hares.push(hare);
game.addChild(hare);
haresSpawned++;
hareSpawnTimer = 0;
}
}
// Spawn birds (only after wave 15) with random variations
if (waveNumber >= 15) {
birdSpawnTimer += gameSpeedMultiplier;
var randomBirdDelay = Math.random() * 35;
if (birdSpawnTimer >= birdSpawnRate + randomBirdDelay && birdsSpawned < maxBirdsPerWave) {
var bird = new Bird();
bird.x = gamePath[0].x + (Math.random() - 0.5) * 450;
bird.y = gamePath[0].y + (Math.random() - 0.5) * 200;
// Random health variation (±25%)
var baseHealth = 400 + waveNumber * 100;
bird.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.5;
bird.maxHealth = bird.health;
// Random speed variation (±30%)
bird.speed = bird.speed * (0.7 + Math.random() * 0.6);
bird.updateHealthBar();
birds.push(bird);
game.addChild(bird);
birdsSpawned++;
birdSpawnTimer = 0;
}
}
// Spawn dogs (only after wave 20) with random variations
if (waveNumber >= 20) {
dogSpawnTimer += gameSpeedMultiplier;
var randomDogDelay = Math.random() * 60;
if (dogSpawnTimer >= dogSpawnRate + randomDogDelay && dogsSpawned < maxDogsPerWave) {
var dog = new Dog();
dog.x = gamePath[0].x + (Math.random() - 0.5) * 400;
dog.y = gamePath[0].y + (Math.random() - 0.5) * 200;
// Random health variation (±35%)
var baseHealth = 800 + waveNumber * 200;
dog.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.7;
dog.maxHealth = dog.health;
// Random speed variation (±25%)
dog.speed = dog.speed * (0.75 + Math.random() * 0.5);
dog.updateHealthBar();
dogs.push(dog);
game.addChild(dog);
dogsSpawned++;
dogSpawnTimer = 0;
}
}
// Check if wave is complete
if (miceSpawned >= maxMicePerWave && ratsSpawned >= maxRatsPerWave && haresSpawned >= maxHaresPerWave && birdsSpawned >= maxBirdsPerWave && dogsSpawned >= maxDogsPerWave && mice.length === 0 && rats.length === 0 && hares.length === 0 && birds.length === 0 && dogs.length === 0) {
// Check if wave 4, 14, or 19 just completed
if (waveNumber === 4 || waveNumber === 14 || waveNumber === 19) {
isPaused = true;
pauseTimer = 0;
}
waveNumber++;
miceSpawned = 0;
ratsSpawned = 0;
haresSpawned = 0;
birdsSpawned = 0;
dogsSpawned = 0;
// Random variation in mice count per wave (±30%)
var baseMiceIncrease = waveNumber <= 3 ? 2 : 1;
maxMicePerWave += Math.floor(baseMiceIncrease * (0.7 + Math.random() * 0.6));
// Start spawning rats after wave 5 with random counts
if (waveNumber >= 5) {
var baseRats = Math.max(1, waveNumber - 4);
maxRatsPerWave = Math.floor(baseRats * (0.8 + Math.random() * 0.4));
}
// Start spawning birds after wave 15 with random counts
if (waveNumber >= 15) {
var baseBirds = Math.max(1, waveNumber - 14);
maxBirdsPerWave = Math.floor(baseBirds * (0.7 + Math.random() * 0.6));
}
// Start spawning hares after wave 10 with random counts
if (waveNumber >= 10) {
var baseHares = Math.max(1, waveNumber - 9);
maxHaresPerWave = Math.floor(baseHares * (0.8 + Math.random() * 0.4));
}
// Start spawning dogs after wave 20 with random counts
if (waveNumber >= 20) {
var baseDogs = Math.max(1, waveNumber - 19);
maxDogsPerWave = Math.floor(baseDogs * (0.6 + Math.random() * 0.8));
}
// Random spawn rate variations
var baseSpawnDecrease = waveNumber <= 3 ? 8 : 12;
spawnRate = Math.max(40, spawnRate - Math.floor(baseSpawnDecrease * (0.8 + Math.random() * 0.4)));
ratSpawnRate = Math.max(90, ratSpawnRate - Math.floor(15 * (0.7 + Math.random() * 0.6)));
hareSpawnRate = Math.max(100, hareSpawnRate - Math.floor(10 * (0.8 + Math.random() * 0.4)));
birdSpawnRate = Math.max(70, birdSpawnRate - Math.floor(10 * (0.9 + Math.random() * 0.2)));
dogSpawnRate = Math.max(120, dogSpawnRate - Math.floor(10 * (0.7 + Math.random() * 0.6)));
updateWaveDisplay();
}
// Update all game objects
for (var i = mice.length - 1; i >= 0; i--) {
if (mice[i].parent) {
mice[i].update();
}
}
for (var i = rats.length - 1; i >= 0; i--) {
if (rats[i].parent) {
rats[i].update();
}
}
for (var i = hares.length - 1; i >= 0; i--) {
if (hares[i].parent) {
hares[i].update();
}
}
for (var i = birds.length - 1; i >= 0; i--) {
if (birds[i].parent) {
birds[i].update();
}
}
for (var i = dogs.length - 1; i >= 0; i--) {
if (dogs[i].parent) {
dogs[i].update();
}
}
for (var i = towers.length - 1; i >= 0; i--) {
if (towers[i].parent) {
towers[i].update();
// Check if tower is touching menu area (bottom 200px of screen)
var isTouchingMenu = towers[i].y > 2532; // Menu area starts around y=2532
if (isTouchingMenu && !isDragging) {
// Check if tower is very close to the path (excluding treatpacket position)
var minDistanceFromPath = Infinity;
for (var p = 0; p < gamePath.length - 1; p++) {
var dx = gamePath[p].x - towers[i].x;
var dy = gamePath[p].y - towers[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistanceFromPath) {
minDistanceFromPath = distance;
}
}
// If not very close to path (more than 120px away), turn red
if (minDistanceFromPath > 120) {
towers[i].tint = 0xff0000; // Red tint
} else {
towers[i].tint = 0xffffff; // Reset to normal
}
} else if (!isDragging && towers[i] !== selectedTower) {
// Reset tint if not touching menu and not being dragged/selected
towers[i].tint = 0xffffff;
}
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 250;
self.maxHealth = 250;
self.speed = 3.0 + Math.random() * 1.0; // Faster than other enemies
self.pathIndex = 0;
self.reward = 35; // Medium reward
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -55;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -55;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
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++;
if (self.pathIndex >= gamePath.length) {
// Bird reached the end - damage packet (8% of current health)
packetHealth -= Math.floor(packetHealth * 0.08);
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
birds.splice(birds.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction
if (dx > 0) {
birdGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
birdGraphics.scaleX = -1; // Face left
}
self.x += dx / distance * self.speed * gameSpeedMultiplier;
self.y += dy / distance * self.speed * gameSpeedMultiplier;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Bird defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
birds.splice(birds.indexOf(self), 1);
}
};
return self;
});
var Dog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('dog', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 400;
self.maxHealth = 400;
self.speed = 1.2 + Math.random() * 0.8; // Slower than most enemies
self.pathIndex = 0;
self.reward = 50; // High reward
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -70;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -70;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
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++;
if (self.pathIndex >= gamePath.length) {
// Dog reached the end - damage packet (20% of current health)
packetHealth -= Math.floor(packetHealth * 0.20);
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
dogs.splice(dogs.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction
if (dx > 0) {
dogGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
dogGraphics.scaleX = -1; // Face left
}
self.x += dx / distance * self.speed * gameSpeedMultiplier;
self.y += dy / distance * self.speed * gameSpeedMultiplier;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Dog defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
dogs.splice(dogs.indexOf(self), 1);
}
};
return self;
});
var Hare = Container.expand(function () {
var self = Container.call(this);
var hareGraphics = self.attachAsset('hare', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 300;
self.maxHealth = 300;
self.speed = 2.0 + Math.random() * 1.0; // Faster than rats
self.pathIndex = 0;
self.reward = 40; // Higher reward than rats
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -65;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -65;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
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++;
if (self.pathIndex >= gamePath.length) {
// Hare reached the end - damage packet (15% of original damage)
packetHealth -= Math.floor(packetHealth * 0.15); // 15% of current packet health
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
hares.splice(hares.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction like cats
if (dx > 0) {
hareGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
hareGraphics.scaleX = -1; // Face left
}
self.x += dx / distance * self.speed * gameSpeedMultiplier;
self.y += dy / distance * self.speed * gameSpeedMultiplier;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Hare defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
hares.splice(hares.indexOf(self), 1);
}
};
return self;
});
var Mouse = Container.expand(function () {
var self = Container.call(this);
var mouseGraphics = self.attachAsset('mouse', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 1.5 + Math.random() * 1.5; // Random speed between 1.5 and 3
self.pathIndex = 0;
self.reward = 10;
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
// Add random wandering to target position for more chaotic movement
var randomOffsetX = (Math.random() - 0.5) * 80;
var randomOffsetY = (Math.random() - 0.5) * 80;
var dx = target.x + randomOffsetX - self.x;
var dy = target.y + randomOffsetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15 + Math.random() * 10) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length) {
// Mouse reached the end - damage packet (5% of original damage)
packetHealth -= 5; // Changed to 5 damage (5% of original 100)
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
mice.splice(mice.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction like cats
if (dx > 0) {
mouseGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
mouseGraphics.scaleX = -1; // Face left
}
// Add slight random jitter to movement for more chaotic behavior
var jitterX = (Math.random() - 0.5) * 2;
var jitterY = (Math.random() - 0.5) * 2;
self.x += dx / distance * self.speed * gameSpeedMultiplier + jitterX;
self.y += dy / distance * self.speed * gameSpeedMultiplier + jitterY;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Mouse defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
mice.splice(mice.indexOf(self), 1);
}
};
return self;
});
var Rat = Container.expand(function () {
var self = Container.call(this);
var ratGraphics = self.attachAsset('rat', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.speed = 1.0 + Math.random() * 1.0; // Slower than before, similar to mice speed
self.pathIndex = 0;
self.reward = 25; // Higher reward
// Create health bar components
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -60;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -35;
healthBarFill.y = -60;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
};
self.update = function () {
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
// Add random wandering behavior for more unpredictable movement
var randomOffsetX = (Math.random() - 0.5) * 100;
var randomOffsetY = (Math.random() - 0.5) * 100;
var dx = target.x + randomOffsetX - self.x;
var dy = target.y + randomOffsetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20 + Math.random() * 15) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length) {
// Rat reached the end - damage packet (10% of original damage)
packetHealth -= 10; // Changed to 10 damage (10% of original 100)
updatePacketDisplay();
LK.getSound('enemySteal').play();
if (packetHealth <= 0) {
LK.showGameOver();
}
self.destroy();
rats.splice(rats.indexOf(self), 1);
return;
}
} else {
// Flip scaleX based on movement direction like cats
if (dx > 0) {
ratGraphics.scaleX = 1; // Face right
} else if (dx < 0) {
ratGraphics.scaleX = -1; // Face left
}
// Add random movement variation for more chaotic behavior
var jitterX = (Math.random() - 0.5) * 3;
var jitterY = (Math.random() - 0.5) * 3;
self.x += dx / distance * self.speed * gameSpeedMultiplier + jitterX;
self.y += dy / distance * self.speed * gameSpeedMultiplier + jitterY;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
// Rat defeated - give reward
gameGold += self.reward;
updateGoldDisplay();
LK.getSound('mouseHit').play();
self.destroy();
rats.splice(rats.indexOf(self), 1);
}
};
return self;
});
var Scratch = Container.expand(function (x, y) {
var self = Container.call(this);
self.attachAsset('scratch', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.rotation = Math.random() * Math.PI * 2;
self.alpha = 1;
var startScale = Math.random() * 0.5 + 0.8;
self.scale.set(startScale);
tween(self, {
alpha: 0,
scaleX: self.scale.x * 1.5,
scaleY: self.scale.y * 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.destroy();
}
}
});
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
var towerGraphics;
self.type = type || 'tired';
self.range = 450; // Increased base range for farther attacks
self.damage = 18; // Reduced from 25
self.fireRate = 30; // frames between shots (0.5 seconds at 60fps)
self.lastShot = 0;
if (self.type === 'tired') {
towerGraphics = self.attachAsset('tiredCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 20; // Increased from 19 to make game winnable
self.cost = 30;
self.fireRate = 60; // Slower attack rate for tired cats
self.range = 450; // Increased range for farther attacks
} else if (self.type === 'normal') {
towerGraphics = self.attachAsset('normalCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 55; // Increased from 45 to make normal cats even stronger
self.range = 500; // Increased range for farther attacks
self.cost = 150;
self.fireRate = 20; // Reduced from 30 to make normal cats faster
} else if (self.type === 'strong') {
towerGraphics = self.attachAsset('strongCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 35; // Reduced from 50
self.range = 550; // Increased range for farther attacks
self.cost = 150;
self.fireRate = 30;
} else if (self.type === 'strongest') {
towerGraphics = self.attachAsset('strongestCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 70; // Reduced from 100
self.range = 600; // Increased range for farther attacks
self.fireRate = 30;
self.cost = 250;
} else if (self.type === 'wild') {
towerGraphics = self.attachAsset('wildCat', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 50;
self.range = 580; // Increased range for farther attacks
self.fireRate = 20; // Faster than other cats
self.cost = 350;
} else if (self.type === 'tiger') {
towerGraphics = self.attachAsset('tiger', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 80;
self.range = 650; // Increased range for farther attacks
self.fireRate = 15; // Very fast attack rate
self.cost = 500;
}
self.update = function () {
// Don't allow towers being dragged to attack (prevents cheating)
if (isDragging && self === draggedTower) {
return;
}
self.lastShot += gameSpeedMultiplier;
var target = self.findTarget();
if (target) {
var currentFireRate = self.fireRate;
// Normal cat attacks rats extremely fast but mice slowly
if (self.type === 'normal') {
if (target.constructor.name === 'Rat') {
currentFireRate = 2; // Extremely fast against rats for superior effectiveness
} else {
currentFireRate = 60; // Slow attack rate against mice and other enemies
}
}
// Wild cat attacks hares and birds extremely fast but other enemies slowly
if (self.type === 'wild') {
if (target.constructor.name === 'Hare' || target.constructor.name === 'Bird') {
currentFireRate = 1; // Ultra fast against hares and birds for superior effectiveness
} else {
currentFireRate = 60; // Slow attack rate against mice and other enemies
}
}
// Tiger attacks dogs much faster than other enemies
if (self.type === 'tiger') {
if (target.constructor.name === 'Dog') {
currentFireRate = 5; // Much faster against dogs
} else {
currentFireRate = 20; // Normal fast rate against other enemies
}
}
if (self.lastShot >= currentFireRate) {
self.shoot(target);
self.lastShot = 0;
}
}
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
// Prioritize targets based on tower type
// Wild cats prioritize birds and hares
if (self.type === 'wild') {
// Check birds first
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
var distance = Math.sqrt(Math.pow(bird.x - self.x, 2) + Math.pow(bird.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = bird;
closestDistance = distance;
}
}
// Check hares second
for (var i = 0; i < hares.length; i++) {
var hare = hares[i];
var distance = Math.sqrt(Math.pow(hare.x - self.x, 2) + Math.pow(hare.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = hare;
closestDistance = distance;
}
}
} else if (self.type === 'tiger') {
// Tigers prioritize dogs
for (var i = 0; i < dogs.length; i++) {
var dog = dogs[i];
var distance = Math.sqrt(Math.pow(dog.x - self.x, 2) + Math.pow(dog.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = dog;
closestDistance = distance;
}
}
}
// Check all enemy types for remaining targeting
// Check mice
for (var i = 0; i < mice.length; i++) {
var mouse = mice[i];
var distance = Math.sqrt(Math.pow(mouse.x - self.x, 2) + Math.pow(mouse.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = mouse;
closestDistance = distance;
}
}
// Check rats
for (var i = 0; i < rats.length; i++) {
var rat = rats[i];
var distance = Math.sqrt(Math.pow(rat.x - self.x, 2) + Math.pow(rat.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = rat;
closestDistance = distance;
}
}
// Check hares
for (var i = 0; i < hares.length; i++) {
var hare = hares[i];
var distance = Math.sqrt(Math.pow(hare.x - self.x, 2) + Math.pow(hare.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = hare;
closestDistance = distance;
}
}
// Check birds
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
var distance = Math.sqrt(Math.pow(bird.x - self.x, 2) + Math.pow(bird.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = bird;
closestDistance = distance;
}
}
// Check dogs
for (var i = 0; i < dogs.length; i++) {
var dog = dogs[i];
var distance = Math.sqrt(Math.pow(dog.x - self.x, 2) + Math.pow(dog.y - self.y, 2));
if (distance < closestDistance) {
closestEnemy = dog;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
// Flip scaleX based on enemy position when shooting
var dx = target.x - self.x;
var dy = target.y - self.y;
// Flip horizontally based on enemy position (face the enemy directly)
var originalScaleX = 1;
if (target.x > self.x) {
originalScaleX = 1; // Face right toward enemy
towerGraphics.scaleX = 1;
} else if (target.x < self.x) {
originalScaleX = -1; // Face left toward enemy
towerGraphics.scaleX = -1;
}
// Calculate damage multiplier
var damageMultiplier = 1;
// Wild cats are excellent against birds and hares
if (self.type === 'wild' && (target.constructor.name === 'Hare' || target.constructor.name === 'Bird')) {
damageMultiplier = 2.0; // 100% bonus damage
// Predict target's next position for better accuracy
var predictedX = target.x + (dx > 0 ? target.speed * 3 : -target.speed * 3);
var predictedY = target.y + (dy > 0 ? target.speed * 3 : -target.speed * 3);
// Apply damage with slight delay for better visual effect
LK.setTimeout(function () {
if (target && target.parent) {
target.takeDamage(self.damage * damageMultiplier);
}
}, 50);
} else if (self.type === 'tiger' && target.constructor.name === 'Dog') {
// Tigers are extremely effective against dogs
damageMultiplier = 2.5; // 150% bonus damage
target.takeDamage(self.damage * damageMultiplier);
} else if (self.type === 'tiger') {
// Tigers attack other enemies well but not as good as dogs
damageMultiplier = 1.3; // 30% bonus damage for all other enemies
target.takeDamage(self.damage * damageMultiplier);
} else {
// Normal damage for other combinations
target.takeDamage(self.damage);
}
LK.getSound('shoot').play();
LK.getSound('hit').play();
// Create multiple scratches for visual effect
for (var i = 0; i < 3; i++) {
var scratchEffect = new Scratch(target.x + (Math.random() - 0.5) * 50, target.y + (Math.random() - 0.5) * 50);
game.addChild(scratchEffect);
}
// Smooth recoil animation - reduced intensity for tigers to prevent trembling
var recoilIntensity = self.type === 'tiger' ? 0.95 : 0.8;
var recoilDuration = self.type === 'tiger' ? 150 : 100;
tween(towerGraphics, {
scaleX: originalScaleX * recoilIntensity,
scaleY: recoilIntensity
}, {
duration: recoilDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: originalScaleX,
scaleY: 1
}, {
duration: recoilDuration,
easing: tween.easeInOut
});
}
});
};
self.getSellPrice = function () {
return Math.floor(self.cost * 0.7); // 70% of original cost
};
self.down = function (x, y, obj) {
if (!gameStarted && !isDragging) {
// Allow repositioning before game starts
if (selectedTower) {
selectedTower.tint = 0xffffff; // Reset previous selection tint
}
selectedTower = self;
self.tint = 0x00ffff; // Cyan tint to show selection
isDragging = true;
draggedTower = self;
// Play pickup sound for repositioning
LK.getSound('pickupDefender').play();
// No need to free placement spots since we're using free positioning
} else if (!isDragging) {
showSellUI(self);
}
};
return self;
});
/****
* Initialize Game
****/
// Game state variables
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Add background image
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
background.x = 1024; // Center horizontally (2048/2)
background.y = 1366; // Center vertically (2732/2)
// Game assets
// Import tween plugin for animations
// Game state variables
var mice = [];
var rats = [];
var hares = [];
var birds = [];
var dogs = [];
var towers = [];
var gameGold = 120;
var packetHealth = 100;
var waveNumber = 1;
var miceSpawned = 0;
var ratsSpawned = 0;
var haresSpawned = 0;
var birdsSpawned = 0;
var dogsSpawned = 0;
var maxMicePerWave = 15;
var maxRatsPerWave = 0;
var maxHaresPerWave = 0;
var maxBirdsPerWave = 0;
var maxDogsPerWave = 0;
var spawnTimer = 0;
var ratSpawnTimer = 0;
var hareSpawnTimer = 0;
var birdSpawnTimer = 0;
var dogSpawnTimer = 0;
var spawnRate = 90; // frames between spawns (much faster spawning for harder waves)
var ratSpawnRate = 100; // frames between rat spawns (faster)
var hareSpawnRate = 120; // frames between hare spawns
var birdSpawnRate = 80; // frames between bird spawns (faster due to speed)
var dogSpawnRate = 140; // frames between dog spawns (slower)
var gameStarted = false;
var gameStartTimer = 0;
var isPaused = false;
var pauseTimer = 0;
var pauseText = null;
var gameSpeedMultiplier = 1; // Normal speed = 1, Fast speed = 2
// Create path for mice - random zigzag movement with more randomization
var gamePath = [{
x: 1024 + (Math.random() - 0.5) * 200,
y: 50 + Math.random() * 100
}, {
x: 600 + (Math.random() - 0.5) * 400,
y: 300 + (Math.random() - 0.5) * 200
}, {
x: 1400 + (Math.random() - 0.5) * 400,
y: 600 + (Math.random() - 0.5) * 200
}, {
x: 400 + (Math.random() - 0.5) * 300,
y: 900 + (Math.random() - 0.5) * 200
}, {
x: 1600 + (Math.random() - 0.5) * 300,
y: 1200 + (Math.random() - 0.5) * 200
}, {
x: 800 + (Math.random() - 0.5) * 400,
y: 1500 + (Math.random() - 0.5) * 200
}, {
x: 1200 + (Math.random() - 0.5) * 400,
y: 1800 + (Math.random() - 0.5) * 200
}, {
x: 500 + (Math.random() - 0.5) * 300,
y: 2100 + (Math.random() - 0.5) * 200
}, {
x: 1500 + (Math.random() - 0.5) * 300,
y: 2400 + (Math.random() - 0.5) * 200
}, {
x: 1024 + (Math.random() - 0.5) * 200,
y: 2700 + Math.random() * 100
}];
// Draw path
for (var i = 0; i < gamePath.length; i++) {
var pathSegment = game.addChild(LK.getAsset('path', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
}));
pathSegment.x = gamePath[i].x;
pathSegment.y = gamePath[i].y;
}
// Create packet of treats at the end
var packet = game.addChild(LK.getAsset('packetTreats', {
anchorX: 0.5,
anchorY: 0.5
}));
packet.x = gamePath[gamePath.length - 1].x;
packet.y = gamePath[gamePath.length - 1].y - 200;
// Create placement system - cats can be placed anywhere near the path
var placementSpots = []; // Keep for compatibility but will be used differently
var maxDistanceFromPath = 300; // Maximum distance cats can be placed from path
// Helper function to check if a position is valid for tower placement
function isValidPlacementPosition(x, y) {
// Check if position is within game bounds
if (x < 100 || x > 1948 || y < 100 || y > 2632) {
return false;
}
// Check if position is too close to existing towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var dx = tower.x - x;
var dy = tower.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 120) {
// Minimum distance between towers
return false;
}
}
return true;
}
// UI Elements
var goldText = new Text2(' Gold: ' + gameGold, {
size: 60,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
goldText.anchor.set(0, 0);
goldText.x = 50;
goldText.y = 50;
LK.gui.topLeft.addChild(goldText);
var healthText = new Text2('Packet Health: ' + packetHealth, {
size: 60,
fill: 0xFF0000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
healthText.anchor.set(0.5, 0);
healthText.x = 0;
healthText.y = 50;
LK.gui.top.addChild(healthText);
var waveText = new Text2('Wave: ' + waveNumber, {
size: 60,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
waveText.anchor.set(1, 0);
waveText.x = 0;
waveText.y = 50;
LK.gui.topRight.addChild(waveText);
// Create start button square
var startButton = LK.getAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3,
alpha: 0
});
startButton.x = 0;
startButton.y = 0;
startButton.interactive = false;
LK.gui.center.addChild(startButton);
// Add tutorial images in front of start button
var tutorialCat = LK.getAsset('tiredCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
});
tutorialCat.x = -120;
tutorialCat.y = -20;
LK.gui.center.addChild(tutorialCat);
// Add arrow image pointing from cat to mouse
var tutorialArrow = new Text2('→', {
size: 180,
fill: 0xFFFF00,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
tutorialArrow.anchor.set(0.5, 0.5);
tutorialArrow.x = 0;
tutorialArrow.y = -20;
tutorialArrow.alpha = 0;
LK.gui.center.addChild(tutorialArrow);
// Add mouse image
var tutorialMouse = LK.getAsset('mouse', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0
});
tutorialMouse.x = 120;
tutorialMouse.y = -20;
LK.gui.center.addChild(tutorialMouse);
// Add wave 5 waiting state
var waitingForWave5Start = false;
// Add click handler to start button
startButton.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
// Play start sound
LK.getSound('start').play();
// Smoothly fade out start button and tutorial elements together
tween(startButton, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
startButton.interactive = false;
// Smoothly fade out tutorial images at the same time
tween(tutorialCat, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialArrow, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialMouse, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
} else if (waitingForWave5Start) {
// Wave 5 start button clicked
waitingForWave5Start = false;
isPaused = false;
pauseTimer = 0;
// Play start sound
LK.getSound('start').play();
// Smoothly fade out start button and tutorial elements together
tween(startButton, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
startButton.interactive = false;
// Smoothly fade out tutorial images at the same time
tween(tutorialCat, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialArrow, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
tween(tutorialMouse, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
};
// Show start button after 10 seconds
LK.setTimeout(function () {
if (!gameStarted) {
tween(startButton, {
alpha: 1
}, {
duration: 500
});
// Show tutorial images
tween(tutorialCat, {
alpha: 1
}, {
duration: 500
});
tween(tutorialArrow, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
// Add pulsing effect to make arrow more visible
tween(tutorialArrow, {
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tutorialArrow, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
}
});
tween(tutorialMouse, {
alpha: 1
}, {
duration: 500
});
startButton.interactive = true;
}
}, 10000);
// Tower selection UI
var selectedTowerType = 'normal';
// Create bottom menu background
var menuBackground = LK.getAsset('placementGrid', {
anchorX: 0.5,
anchorY: 1,
scaleX: 20,
scaleY: 2,
alpha: 0.8
});
menuBackground.x = 0;
menuBackground.y = 0;
LK.gui.bottom.addChild(menuBackground);
// Create tired cat button with asset
var tiredCatButton = LK.getAsset('tiredCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
tiredCatButton.x = -200;
tiredCatButton.y = -80;
LK.gui.bottom.addChild(tiredCatButton);
// Create price text for tired cat
var tiredCatPrice = new Text2('30g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
tiredCatPrice.anchor.set(0.5, 0);
tiredCatPrice.x = -200;
tiredCatPrice.y = -40;
LK.gui.bottom.addChild(tiredCatPrice);
// Create normal cat button with asset
var normalCatButton = LK.getAsset('normalCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
normalCatButton.x = 200;
normalCatButton.y = -80;
LK.gui.bottom.addChild(normalCatButton);
// Create price text for normal cat
var normalCatPrice = new Text2('150g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
normalCatPrice.anchor.set(0.5, 0);
normalCatPrice.x = 200;
normalCatPrice.y = -40;
LK.gui.bottom.addChild(normalCatPrice);
// Create wild cat button with asset
var wildCatButton = LK.getAsset('wildCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
wildCatButton.x = 400;
wildCatButton.y = -80;
LK.gui.bottom.addChild(wildCatButton);
// Create price text for wild cat
var wildCatPrice = new Text2('350g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
wildCatPrice.anchor.set(0.5, 0);
wildCatPrice.x = 400;
wildCatPrice.y = -40;
LK.gui.bottom.addChild(wildCatPrice);
// Create tiger button with asset
var tigerButton = LK.getAsset('tiger', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
tigerButton.x = 600;
tigerButton.y = -80;
LK.gui.bottom.addChild(tigerButton);
// Create price text for tiger
var tigerPrice = new Text2('500g', {
size: 40,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
tigerPrice.anchor.set(0.5, 0);
tigerPrice.x = 600;
tigerPrice.y = -40;
LK.gui.bottom.addChild(tigerPrice);
// Create speed control buttons
var fastForwardButton = new Text2('⏩', {
size: 80,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
fastForwardButton.anchor.set(1, 0);
fastForwardButton.x = -20;
fastForwardButton.y = 120;
LK.gui.topRight.addChild(fastForwardButton);
var normalSpeedButton = new Text2('▶️', {
size: 80,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
normalSpeedButton.anchor.set(1, 0);
normalSpeedButton.x = -20;
normalSpeedButton.y = 220;
fastForwardButton.alpha = 1; // Start opaque since fast speed is not selected
normalSpeedButton.alpha = 0.5; // Start transparent since normal speed is selected
LK.gui.topRight.addChild(normalSpeedButton);
// Initialize button transparency
updateTiredCatButtonAlpha();
updateNormalCatButtonAlpha();
updateWildCatButtonAlpha();
updateTigerButtonAlpha();
// Speed control button handlers
fastForwardButton.down = function (x, y, obj) {
gameSpeedMultiplier = 2;
// Make fast button transparent and normal button opaque (clicked option is transparent)
tween(fastForwardButton, {
alpha: 0.5
}, {
duration: 200
});
tween(normalSpeedButton, {
alpha: 1
}, {
duration: 200
});
};
normalSpeedButton.down = function (x, y, obj) {
gameSpeedMultiplier = 1;
// Make normal button transparent and fast button opaque (clicked option is transparent)
tween(fastForwardButton, {
alpha: 1
}, {
duration: 200
});
tween(normalSpeedButton, {
alpha: 0.5
}, {
duration: 200
});
};
// Dragging variables
var isDragging = false;
var draggedTower = null;
var dragOffset = {
x: 0,
y: 0
};
// Sell UI variables
var sellUI = null;
var sellUITower = null;
// Tower selection for repositioning
var selectedTower = null;
function updateGoldDisplay() {
goldText.setText(' Gold: ' + gameGold);
updateTiredCatButtonAlpha();
updateNormalCatButtonAlpha();
updateWildCatButtonAlpha();
updateTigerButtonAlpha();
}
function updatePacketDisplay() {
healthText.setText('Packet Health: ' + packetHealth);
}
function updateWaveDisplay() {
waveText.setText('Wave: ' + waveNumber);
}
function showSellUI(tower) {
// Remove existing sell UI
if (sellUI) {
sellUI.destroy();
sellUI = null;
}
sellUITower = tower;
sellUI = new Container();
game.addChild(sellUI);
// Position sell UI above tower
sellUI.x = tower.x;
sellUI.y = tower.y - 100;
// Create sell button
var sellButton = sellUI.attachAsset('sellButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Create sell price text
var sellPrice = tower.getSellPrice();
var sellText = new Text2('SELL\n' + sellPrice + 'g', {
size: 24,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma",
fontWeight: 'bold'
});
sellText.anchor.set(0.5, 0.5);
sellText.x = 0;
sellText.y = 0;
sellUI.addChild(sellText);
// Sell button click handler
sellButton.down = function (x, y, obj) {
sellTower(sellUITower);
};
// Auto-hide sell UI after 3 seconds
LK.setTimeout(function () {
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
}, 3000);
}
function sellTower(tower) {
// Give back gold (70% of original cost)
var sellPrice = tower.getSellPrice();
gameGold += sellPrice;
updateGoldDisplay();
LK.getSound('mouseHit').play();
// No need to free placement spots since we're using free positioning
// Remove tower from towers array
var towerIndex = towers.indexOf(tower);
if (towerIndex > -1) {
towers.splice(towerIndex, 1);
}
// Destroy tower
tower.destroy();
// Hide sell UI
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
}
// Tired cat button drag functionality
tiredCatButton.down = function (x, y, obj) {
if (gameGold >= 30) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'tired';
// Create dragged tower preview
draggedTower = new Tower('tired');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(tiredCatButton.parent.toGlobal(tiredCatButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(tiredCatButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(tiredCatButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
}
});
}
};
function updateTiredCatButtonAlpha() {
if (gameGold >= 30) {
tiredCatButton.alpha = 1;
tiredCatPrice.alpha = 1;
} else {
tiredCatButton.alpha = 0.5;
tiredCatPrice.alpha = 0.5;
}
}
// Add move handler to tired cat button for drag support
tiredCatButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'tired') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(tiredCatButton.parent.toGlobal(tiredCatButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Normal cat button drag functionality
normalCatButton.down = function (x, y, obj) {
if (gameGold >= 150) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'normal';
// Create dragged tower preview
draggedTower = new Tower('normal');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(normalCatButton.parent.toGlobal(normalCatButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(normalCatButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100,
onFinish: function onFinish() {
tween(normalCatButton, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 100
});
}
});
}
};
function updateNormalCatButtonAlpha() {
if (gameGold >= 150) {
normalCatButton.alpha = 1;
normalCatPrice.alpha = 1;
} else {
normalCatButton.alpha = 0.5;
normalCatPrice.alpha = 0.5;
}
}
// Add move handler to normal cat button for drag support
normalCatButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'normal') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(normalCatButton.parent.toGlobal(normalCatButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Wild cat button drag functionality
wildCatButton.down = function (x, y, obj) {
if (gameGold >= 350) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'wild';
// Create dragged tower preview
draggedTower = new Tower('wild');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(wildCatButton.parent.toGlobal(wildCatButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(wildCatButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(wildCatButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100
});
}
});
}
};
function updateWildCatButtonAlpha() {
if (gameGold >= 350) {
wildCatButton.alpha = 1;
wildCatPrice.alpha = 1;
} else {
wildCatButton.alpha = 0.5;
wildCatPrice.alpha = 0.5;
}
}
// Add move handler to wild cat button for drag support
wildCatButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'wild') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(wildCatButton.parent.toGlobal(wildCatButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Tiger button drag functionality
tigerButton.down = function (x, y, obj) {
if (gameGold >= 500) {
// Hide sell UI when starting to drag
if (sellUI) {
sellUI.destroy();
sellUI = null;
sellUITower = null;
}
isDragging = true;
selectedTowerType = 'tiger';
// Create dragged tower preview
draggedTower = new Tower('tiger');
draggedTower.alpha = 0.8;
draggedTower.scaleX = 1.2;
draggedTower.scaleY = 1.2;
game.addChild(draggedTower);
// Convert button position to game coordinates
var buttonPos = game.toLocal(tigerButton.parent.toGlobal(tigerButton.position));
draggedTower.x = buttonPos.x;
draggedTower.y = buttonPos.y;
dragOffset.x = x;
dragOffset.y = y;
// Play pickup sound
LK.getSound('pickupDefender').play();
// Button feedback
tween(tigerButton, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 100,
onFinish: function onFinish() {
tween(tigerButton, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 100
});
}
});
}
};
function updateTigerButtonAlpha() {
if (gameGold >= 500) {
tigerButton.alpha = 1;
tigerPrice.alpha = 1;
} else {
tigerButton.alpha = 0.5;
tigerPrice.alpha = 0.5;
}
}
// Add move handler to tiger button for drag support
tigerButton.move = function (x, y, obj) {
if (isDragging && draggedTower && selectedTowerType === 'tiger') {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Apply the offset from button position
var buttonPos = game.toLocal(tigerButton.parent.toGlobal(tigerButton.position));
draggedTower.x = gamePos.x + (buttonPos.x - dragOffset.x);
draggedTower.y = gamePos.y + (buttonPos.y - dragOffset.y);
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Add up handlers to all tower buttons
tiredCatButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
normalCatButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
wildCatButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
tigerButton.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gamePos = obj.parent ? game.toLocal(obj.parent.toGlobal({
x: x,
y: y
})) : {
x: x,
y: y
};
// Check if current position is valid for placement using dragged tower's position
var isValidPosition = isValidPlacementPosition(draggedTower.x, draggedTower.y);
var towerCost = draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place tower at dragged position (tower already positioned correctly)
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
LK.getSound('placeDefender').play();
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging state
isDragging = false;
draggedTower = null;
}
};
// Game move handler for dragging
game.move = function (x, y, obj) {
if (isDragging && draggedTower) {
draggedTower.x = x;
draggedTower.y = y;
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(x, y);
// Tint tower based on placement validity
if (isValidPosition) {
draggedTower.tint = 0x00ff00; // Green tint for valid placement
} else if (selectedTower === draggedTower) {
draggedTower.tint = 0x00ffff; // Keep cyan tint for selected tower being repositioned
} else {
draggedTower.tint = 0xff0000; // Red tint for invalid placement
}
}
};
// Game up handler for tower placement
game.up = function (x, y, obj) {
if (isDragging && draggedTower) {
// Check if current position is valid for placement
var isValidPosition = isValidPlacementPosition(x, y);
// Check if this is a repositioning of an existing tower
var isRepositioning = selectedTower === draggedTower;
var towerCost = isRepositioning ? 0 : draggedTower.cost;
if (isValidPosition && gameGold >= towerCost) {
// Place or reposition tower at current position
if (!isRepositioning) {
gameGold -= towerCost;
updateGoldDisplay();
towers.push(draggedTower);
}
// Keep tower at current dragged position
draggedTower.alpha = 1;
draggedTower.tint = 0xffffff; // Reset tint
if (!isRepositioning) {
LK.getSound('placeDefender').play();
}
} else if (isRepositioning) {
// If repositioning failed, keep tower at its current position but remove selection
draggedTower.tint = 0xffffff;
} else {
// Remove dragged tower if placement failed
draggedTower.destroy();
}
// Reset dragging and selection state
isDragging = false;
draggedTower = null;
if (selectedTower) {
selectedTower = null;
}
}
};
// Start background music
LK.playMusic('bgMusic');
game.update = function () {
// Don't spawn mice until game starts (start button clicked)
if (!gameStarted) {
return; // Don't spawn mice until game starts
}
// Handle pause after wave 4 (changed from wave 3) or waiting for wave 5 start
if (isPaused || waitingForWave5Start) {
if (isPaused) {
pauseTimer += gameSpeedMultiplier;
if (pauseTimer >= 600) {
// 10 seconds at 60fps
isPaused = false;
// Remove pause text
if (pauseText) {
pauseText.destroy();
pauseText = null;
}
// Show start button with normal cat, arrow, rat after wave 4
if (waveNumber === 5) {
// Wave number increments before pause check
// Set waiting state for wave 5
waitingForWave5Start = true;
// Show start button
startButton.alpha = 1;
startButton.interactive = true;
// Change tutorial images for wave 4 completion
tutorialCat.destroy();
tutorialCat = LK.getAsset('normalCat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 1
});
tutorialCat.x = -120;
tutorialCat.y = -20;
LK.gui.center.addChild(tutorialCat);
tutorialArrow.alpha = 1;
// Change mouse to rat
tutorialMouse.destroy();
tutorialMouse = LK.getAsset('rat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 1
});
tutorialMouse.x = 120;
tutorialMouse.y = -20;
LK.gui.center.addChild(tutorialMouse);
}
}
}
return; // Don't spawn enemies during pause or while waiting for wave 5 start
}
// Spawn mice with much more random timing variation
spawnTimer += gameSpeedMultiplier;
var randomSpawnDelay = Math.random() * 60 + Math.random() * 30; // Much more random delay
if (spawnTimer >= spawnRate + randomSpawnDelay && miceSpawned < maxMicePerWave) {
var mouse = new Mouse();
// Much more random spawn positions with larger variation
mouse.x = gamePath[0].x + (Math.random() - 0.5) * 600 + Math.random() * 200;
mouse.y = gamePath[0].y + (Math.random() - 0.5) * 400 + Math.random() * 150;
// More random health variation (±40%)
var baseHealth = 180 + waveNumber * 50;
mouse.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.8;
mouse.maxHealth = mouse.health;
// Much more random speed variation (±50%)
mouse.speed = mouse.speed * (0.5 + Math.random() * 1.0);
mouse.updateHealthBar();
mice.push(mouse);
game.addChild(mouse);
miceSpawned++;
spawnTimer = Math.random() * 20; // Random reset for more chaos
}
// Spawn rats (only after wave 5) with random variations
if (waveNumber >= 5) {
ratSpawnTimer += gameSpeedMultiplier;
var randomRatDelay = Math.random() * 40;
if (ratSpawnTimer >= ratSpawnRate + randomRatDelay && ratsSpawned < maxRatsPerWave) {
var rat = new Rat();
rat.x = gamePath[0].x + (Math.random() - 0.5) * 500;
rat.y = gamePath[0].y + (Math.random() - 0.5) * 250;
// Random health variation (±25%)
var baseHealth = 500 + waveNumber * 120;
rat.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.5;
rat.maxHealth = rat.health;
// Random speed variation (±40%)
rat.speed = rat.speed * (0.6 + Math.random() * 0.8);
rat.updateHealthBar();
rats.push(rat);
game.addChild(rat);
ratsSpawned++;
ratSpawnTimer = 0;
}
}
// Spawn hares (only after wave 10) with random variations
if (waveNumber >= 10) {
hareSpawnTimer += gameSpeedMultiplier;
var randomHareDelay = Math.random() * 50;
if (hareSpawnTimer >= hareSpawnRate + randomHareDelay && haresSpawned < maxHaresPerWave) {
var hare = new Hare();
hare.x = gamePath[0].x + (Math.random() - 0.5) * 600;
hare.y = gamePath[0].y + (Math.random() - 0.5) * 300;
// Random health variation (±30%)
var baseHealth = 700 + waveNumber * 150;
hare.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.6;
hare.maxHealth = hare.health;
// Random speed variation (±35%)
hare.speed = hare.speed * (0.65 + Math.random() * 0.7);
hare.updateHealthBar();
hares.push(hare);
game.addChild(hare);
haresSpawned++;
hareSpawnTimer = 0;
}
}
// Spawn birds (only after wave 15) with random variations
if (waveNumber >= 15) {
birdSpawnTimer += gameSpeedMultiplier;
var randomBirdDelay = Math.random() * 35;
if (birdSpawnTimer >= birdSpawnRate + randomBirdDelay && birdsSpawned < maxBirdsPerWave) {
var bird = new Bird();
bird.x = gamePath[0].x + (Math.random() - 0.5) * 450;
bird.y = gamePath[0].y + (Math.random() - 0.5) * 200;
// Random health variation (±25%)
var baseHealth = 400 + waveNumber * 100;
bird.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.5;
bird.maxHealth = bird.health;
// Random speed variation (±30%)
bird.speed = bird.speed * (0.7 + Math.random() * 0.6);
bird.updateHealthBar();
birds.push(bird);
game.addChild(bird);
birdsSpawned++;
birdSpawnTimer = 0;
}
}
// Spawn dogs (only after wave 20) with random variations
if (waveNumber >= 20) {
dogSpawnTimer += gameSpeedMultiplier;
var randomDogDelay = Math.random() * 60;
if (dogSpawnTimer >= dogSpawnRate + randomDogDelay && dogsSpawned < maxDogsPerWave) {
var dog = new Dog();
dog.x = gamePath[0].x + (Math.random() - 0.5) * 400;
dog.y = gamePath[0].y + (Math.random() - 0.5) * 200;
// Random health variation (±35%)
var baseHealth = 800 + waveNumber * 200;
dog.health = baseHealth + (Math.random() - 0.5) * baseHealth * 0.7;
dog.maxHealth = dog.health;
// Random speed variation (±25%)
dog.speed = dog.speed * (0.75 + Math.random() * 0.5);
dog.updateHealthBar();
dogs.push(dog);
game.addChild(dog);
dogsSpawned++;
dogSpawnTimer = 0;
}
}
// Check if wave is complete
if (miceSpawned >= maxMicePerWave && ratsSpawned >= maxRatsPerWave && haresSpawned >= maxHaresPerWave && birdsSpawned >= maxBirdsPerWave && dogsSpawned >= maxDogsPerWave && mice.length === 0 && rats.length === 0 && hares.length === 0 && birds.length === 0 && dogs.length === 0) {
// Check if wave 4, 14, or 19 just completed
if (waveNumber === 4 || waveNumber === 14 || waveNumber === 19) {
isPaused = true;
pauseTimer = 0;
}
waveNumber++;
miceSpawned = 0;
ratsSpawned = 0;
haresSpawned = 0;
birdsSpawned = 0;
dogsSpawned = 0;
// Random variation in mice count per wave (±30%)
var baseMiceIncrease = waveNumber <= 3 ? 2 : 1;
maxMicePerWave += Math.floor(baseMiceIncrease * (0.7 + Math.random() * 0.6));
// Start spawning rats after wave 5 with random counts
if (waveNumber >= 5) {
var baseRats = Math.max(1, waveNumber - 4);
maxRatsPerWave = Math.floor(baseRats * (0.8 + Math.random() * 0.4));
}
// Start spawning birds after wave 15 with random counts
if (waveNumber >= 15) {
var baseBirds = Math.max(1, waveNumber - 14);
maxBirdsPerWave = Math.floor(baseBirds * (0.7 + Math.random() * 0.6));
}
// Start spawning hares after wave 10 with random counts
if (waveNumber >= 10) {
var baseHares = Math.max(1, waveNumber - 9);
maxHaresPerWave = Math.floor(baseHares * (0.8 + Math.random() * 0.4));
}
// Start spawning dogs after wave 20 with random counts
if (waveNumber >= 20) {
var baseDogs = Math.max(1, waveNumber - 19);
maxDogsPerWave = Math.floor(baseDogs * (0.6 + Math.random() * 0.8));
}
// Random spawn rate variations
var baseSpawnDecrease = waveNumber <= 3 ? 8 : 12;
spawnRate = Math.max(40, spawnRate - Math.floor(baseSpawnDecrease * (0.8 + Math.random() * 0.4)));
ratSpawnRate = Math.max(90, ratSpawnRate - Math.floor(15 * (0.7 + Math.random() * 0.6)));
hareSpawnRate = Math.max(100, hareSpawnRate - Math.floor(10 * (0.8 + Math.random() * 0.4)));
birdSpawnRate = Math.max(70, birdSpawnRate - Math.floor(10 * (0.9 + Math.random() * 0.2)));
dogSpawnRate = Math.max(120, dogSpawnRate - Math.floor(10 * (0.7 + Math.random() * 0.6)));
updateWaveDisplay();
}
// Update all game objects
for (var i = mice.length - 1; i >= 0; i--) {
if (mice[i].parent) {
mice[i].update();
}
}
for (var i = rats.length - 1; i >= 0; i--) {
if (rats[i].parent) {
rats[i].update();
}
}
for (var i = hares.length - 1; i >= 0; i--) {
if (hares[i].parent) {
hares[i].update();
}
}
for (var i = birds.length - 1; i >= 0; i--) {
if (birds[i].parent) {
birds[i].update();
}
}
for (var i = dogs.length - 1; i >= 0; i--) {
if (dogs[i].parent) {
dogs[i].update();
}
}
for (var i = towers.length - 1; i >= 0; i--) {
if (towers[i].parent) {
towers[i].update();
// Check if tower is touching menu area (bottom 200px of screen)
var isTouchingMenu = towers[i].y > 2532; // Menu area starts around y=2532
if (isTouchingMenu && !isDragging) {
// Check if tower is very close to the path (excluding treatpacket position)
var minDistanceFromPath = Infinity;
for (var p = 0; p < gamePath.length - 1; p++) {
var dx = gamePath[p].x - towers[i].x;
var dy = gamePath[p].y - towers[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistanceFromPath) {
minDistanceFromPath = distance;
}
}
// If not very close to path (more than 120px away), turn red
if (minDistanceFromPath > 120) {
towers[i].tint = 0xff0000; // Red tint
} else {
towers[i].tint = 0xffffff; // Reset to normal
}
} else if (!isDragging && towers[i] !== selectedTower) {
// Reset tint if not touching menu and not being dragged/selected
towers[i].tint = 0xffffff;
}
}
}
};
Un emignone souris cartoon style sur ses quatres pattes. In-Game asset. 2d. High contrast. No shadows
Cute tired cat manga cartoon style. In-Game asset. 2d. High contrast. No shadows
A cute manga style cat. In-Game asset. 2d. High contrast. No shadows
An angry but cute wild cat manga cartoon style. In-Game asset. 2d. High contrast. No shadows
A big angry cute tiger. In-Game asset. 2d. High contrast. No shadows
A pretty air view grass plain. In-Game asset. 2d. High contrast. No shadows
A red rectangular button. In-Game asset. 2d. High contrast. No shadows
A cute hare running on 4 paws. In-Game asset. 2d. High contrast. No shadows
cute lil bird flying cartoon style
Make more manga style and cutter with cut colors
a cute angry dog manga style. In-Game asset. 2d. High contrast. No shadows