/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 1; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { self.x = self.targetX; self.y = self.targetY; self.isAtTarget = true; } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var Defender = Container.expand(function () { var self = Container.call(this); self.range = 200; self.fireRate = 60; self.lastFireTime = 0; self.damage = 1; self.isPlaced = false; self.canFire = function () { return LK.ticks - self.lastFireTime > self.fireRate; }; self.findTarget = function () { var closestEnemy = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead) { continue; } var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range && distance < closestDistance) { closestEnemy = enemy; closestDistance = distance; } } return closestEnemy; }; self.shoot = function (target) { if (!self.canFire()) { return; } var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; bullets.push(bullet); game.addChild(bullet); self.lastFireTime = LK.ticks; LK.getSound('shoot').play(); }; self.update = function () { if (!self.isPlaced) { return; } var target = self.findTarget(); if (target) { self.shoot(target); } }; return self; }); var Raccoon = Defender.expand(function () { var self = Defender.call(this); var graphics = self.attachAsset('raccoon', { anchorX: 0.5, anchorY: 0.5 }); self.range = 350; self.fireRate = 40; self.damage = 2.5; return self; }); var Cat = Defender.expand(function () { var self = Defender.call(this); var graphics = self.attachAsset('cat', { anchorX: 0.5, anchorY: 0.5 }); self.range = 400; self.fireRate = 50; self.damage = 1.5; return self; }); var Bird = Defender.expand(function () { var self = Defender.call(this); var graphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); self.range = 450; self.fireRate = 80; self.damage = 0.5; return self; }); var Bear = Defender.expand(function () { var self = Defender.call(this); var graphics = self.attachAsset('bear', { anchorX: 0.5, anchorY: 0.5 }); self.range = 300; self.fireRate = 70; self.damage = 4; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); self.speed = 2; self.health = 1; self.maxHealth = 1; self.isDead = false; self.targetX = 0; self.targetY = 0; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.isDead = true; LK.setScore(LK.getScore() + 10); money += self.coinReward || 1; updateMoneyDisplay(); LK.getSound('enemyHit').play(); } }; self.update = function () { if (self.isDead) { return; } var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { self.x = self.targetX; self.y = self.targetY; self.hasReachedMeat = true; } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var Worm = Enemy.expand(function () { var self = Enemy.call(this); var graphics = self.attachAsset('worm', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; self.health = 1; self.maxHealth = 1; self.coinReward = 1; return self; }); var Snake = Enemy.expand(function () { var self = Enemy.call(this); var graphics = self.attachAsset('snake', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.8; self.health = 3; self.maxHealth = 3; self.coinReward = 3; return self; }); var Mouse = Enemy.expand(function () { var self = Enemy.call(this); var graphics = self.attachAsset('mouse', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2.5; self.health = 2; self.maxHealth = 2; self.coinReward = 2; return self; }); var Ant = Enemy.expand(function () { var self = Enemy.call(this); var graphics = self.attachAsset('ant', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.health = 1; self.maxHealth = 1; self.coinReward = 1; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ var grassBackground = game.addChild(LK.getAsset('grass', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); var meat = game.addChild(LK.getAsset('meat', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 1.5, scaleY: 1.5 })); var enemies = []; var defenders = []; var bullets = []; var money = 7; var selectedDefenderType = 'bird'; var rangeIndicator = null; var showingRange = false; var waveNumber = 1; var enemiesSpawned = 0; var enemiesPerWave = 5; var spawnTimer = 0; var spawnInterval = 120; var defenderCounts = { 'bird': 0, 'cat': 0, 'raccoon': 0, 'bear': 0 }; var defenderLimits = { 'bird': 5, 'cat': 4, 'raccoon': 3, 'bear': 2 }; var waveTxt = new Text2('Wave: 1', { size: 50, fill: 0xFFFFFF }); waveTxt.anchor.set(0, 0); waveTxt.x = 200; waveTxt.y = 100; LK.gui.topLeft.addChild(waveTxt); var moneyTxt = new Text2('Money: 7', { size: 50, fill: 0xFFD700 }); moneyTxt.anchor.set(0, 0); moneyTxt.x = 200; moneyTxt.y = 160; LK.gui.topLeft.addChild(moneyTxt); var selectedDefenderType = 'bird'; var defenderTypes = ['bird', 'cat', 'raccoon', 'bear']; var defenderCosts = { 'bird': 1, 'cat': 4, 'raccoon': 8, 'bear': 12 }; var defenderButtons = []; function updateWave() { waveTxt.setText('Wave: ' + waveNumber); } function updateMoneyDisplay() { moneyTxt.setText('Money: ' + money); updateDefenderButtonStates(); } function createDefenderButtons() { var buttonSize = 180; var spacing = 60; var totalWidth = defenderTypes.length * buttonSize + (defenderTypes.length - 1) * spacing; var startX = (2048 - totalWidth) / 2; var y = 2732 - buttonSize - 50; for (var i = 0; i < defenderTypes.length; i++) { var defenderType = defenderTypes[i]; var cost = defenderCosts[defenderType]; // Create button container var button = new Container(); button.defenderType = defenderType; button.cost = cost; button.x = startX + i * (buttonSize + spacing); button.y = y; button.width = buttonSize; button.height = buttonSize + 60; // Extra height for text // Background removed for cleaner look // Add defender image var defenderImage = LK.getAsset(defenderType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); button.addChild(defenderImage); // Add cost text var costText = new Text2('$' + cost, { size: 60, fill: 0xFFD700 }); costText.anchor.set(0.5, 0); costText.x = 0; costText.y = buttonSize / 2 + 15; button.addChild(costText); button.costText = costText; // Add count/limit text var countText = new Text2('0/' + defenderLimits[defenderType], { size: 40, fill: 0xFFFFFF }); countText.anchor.set(0.5, 0); countText.x = 0; countText.y = buttonSize / 2 + 75; button.addChild(countText); button.countText = countText; // Add selection indicator var selectionIndicator = LK.getAsset('rangeIndicator', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7, alpha: 0.6 }); selectionIndicator.visible = false; button.addChild(selectionIndicator); button.selectionIndicator = selectionIndicator; defenderButtons.push(button); game.addChild(button); } updateDefenderButtonStates(); } function updateDefenderButtonStates() { for (var i = 0; i < defenderButtons.length; i++) { var button = defenderButtons[i]; var canAfford = money >= button.cost; var isSelected = button.defenderType === selectedDefenderType; var hasReachedLimit = defenderCounts[button.defenderType] >= defenderLimits[button.defenderType]; // Update affordability colors and limits if (hasReachedLimit) { button.costText.fill = 0x888888; // Gray for limit reached button.alpha = 0.3; } else { button.costText.fill = canAfford ? 0xFFD700 : 0xFF0000; button.alpha = canAfford ? 1.0 : 0.5; } // Update selection indicator button.selectionIndicator.visible = isSelected; // Update count text button.countText.setText(defenderCounts[button.defenderType] + '/' + defenderLimits[button.defenderType]); } } createDefenderButtons(); var snakeSpawnedThisWave = false; function spawnEnemy() { var enemyTypes = [Worm, Ant, Mouse]; if (waveNumber < 5) { // Before wave 5: Snake can spawn once per wave if (!snakeSpawnedThisWave && Math.random() < 0.2) { enemyTypes.push(Snake); } } else { // Wave 5 and after: Snake can spawn unlimited enemyTypes.push(Snake); } var EnemyClass = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; if (EnemyClass === Snake && waveNumber < 5) { snakeSpawnedThisWave = true; } var enemy = new EnemyClass(); // Boost enemy health after wave 5 if (waveNumber > 5) { var healthBoost = Math.floor((waveNumber - 5) / 2) + 1; // +1 HP every 2 waves after wave 5 enemy.health += healthBoost; enemy.maxHealth += healthBoost; } var spawnSides = [{ x: 0, y: Math.random() * 2732, flipX: 1 // Left side - face right (flip) }, { x: 2048, y: Math.random() * 2732, flipX: 0 // Right side - face left (no flip) }, { x: Math.random() * 2048, y: 0, flipX: 0 // Top side - no flip }, { x: Math.random() * 2048, y: 2732, flipX: 0 // Bottom side - no flip }]; var spawn = spawnSides[Math.floor(Math.random() * spawnSides.length)]; enemy.x = spawn.x; enemy.y = spawn.y; enemy.targetX = meat.x; enemy.targetY = meat.y; // Apply horizontal flip based on spawn side and position relative to meat if (spawn.flipX === 1) { enemy.scaleX = -1; } else if (spawn.x === 0 || spawn.x === 2048) { // Left or right spawn - already handled above } else { // Top or bottom spawn - face towards meat horizontally if (enemy.x > meat.x) { // Enemy is to the right of meat - face left enemy.scaleX = -1; } else { // Enemy is to the left of meat - face right enemy.scaleX = 1; } } enemies.push(enemy); game.addChild(enemy); enemiesSpawned++; } function startNextWave() { waveNumber++; enemiesSpawned = 0; enemiesPerWave = Math.min((5 + Math.floor(waveNumber / 2)) * 2, 30); spawnInterval = Math.max(60 - waveNumber * 5, 20); snakeSpawnedThisWave = false; updateWave(); } function showRangeIndicator(x, y) { if (rangeIndicator) { rangeIndicator.destroy(); } rangeIndicator = LK.getAsset('rangeIndicator', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, alpha: 0.3 }); var range = 450; if (selectedDefenderType === 'bird') { range = 450; } else if (selectedDefenderType === 'bear') { range = 300; } else if (selectedDefenderType === 'cat') { range = 400; } else if (selectedDefenderType === 'raccoon') { range = 350; } rangeIndicator.width = range * 2; rangeIndicator.height = range * 2; game.addChild(rangeIndicator); showingRange = true; } function hideRangeIndicator() { if (rangeIndicator) { rangeIndicator.destroy(); rangeIndicator = null; } showingRange = false; } game.down = function (x, y, obj) { // Check if clicked on a defender button for (var i = 0; i < defenderButtons.length; i++) { var button = defenderButtons[i]; var buttonCenterX = button.x; var buttonCenterY = button.y; var buttonWidth = 180; var buttonHeight = 240; // Including text area // Check if click is within button bounds if (x >= buttonCenterX - buttonWidth / 2 && x <= buttonCenterX + buttonWidth / 2 && y >= buttonCenterY - buttonHeight / 2 && y <= buttonCenterY + buttonHeight / 2) { // Check if player can afford this defender and hasn't reached limit if (money >= button.cost && defenderCounts[button.defenderType] < defenderLimits[button.defenderType]) { selectedDefenderType = button.defenderType; updateDefenderButtonStates(); } return; } } // Check if valid placement area (not too close to edges or meat) if (y < 150 || y > 2732 - 200) { return; } // Too close to top or bottom if (x < 100 || x > 2048 - 100) { return; } // Too close to sides // Check if too close to meat var distanceToMeat = Math.sqrt((x - meat.x) * (x - meat.x) + (y - meat.y) * (y - meat.y)); if (distanceToMeat < 150) { return; } // Too close to meat // Check if space is already occupied by another defender var spaceTaken = false; for (var i = 0; i < defenders.length; i++) { var defender = defenders[i]; var distance = Math.sqrt((x - defender.x) * (x - defender.x) + (y - defender.y) * (y - defender.y)); if (distance < 100) { spaceTaken = true; break; } } if (spaceTaken) { return; } // Check if player has enough money and hasn't reached limit var cost = defenderCosts[selectedDefenderType]; if (money < cost) { return; } if (defenderCounts[selectedDefenderType] >= defenderLimits[selectedDefenderType]) { return; } // Place defender var DefenderClass; if (selectedDefenderType === 'bird') { DefenderClass = Bird; } else if (selectedDefenderType === 'bear') { DefenderClass = Bear; } else if (selectedDefenderType === 'cat') { DefenderClass = Cat; } else if (selectedDefenderType === 'raccoon') { DefenderClass = Raccoon; } var newDefender = new DefenderClass(); newDefender.x = x; newDefender.y = y; newDefender.isPlaced = true; // Face defender towards meat if (x > meat.x) { // Right side - face left (flip) newDefender.scaleX = -1; } else { // Left side - face right (no flip) newDefender.scaleX = 1; } defenders.push(newDefender); game.addChild(newDefender); // Deduct money and increment count money -= cost; defenderCounts[selectedDefenderType]++; updateMoneyDisplay(); // Show range indicator briefly showRangeIndicator(x, y); LK.setTimeout(function () { hideRangeIndicator(); }, 1000); }; game.update = function () { if (enemiesSpawned < enemiesPerWave) { spawnTimer++; if (spawnTimer >= spawnInterval) { spawnEnemy(); spawnTimer = 0; } } for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.isAtTarget) { bullet.destroy(); bullets.splice(i, 1); continue; } var hitEnemy = false; for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; if (enemy.isDead) { continue; } if (bullet.intersects(enemy)) { enemy.takeDamage(bullet.damage); hitEnemy = true; break; } } if (hitEnemy) { bullet.destroy(); bullets.splice(i, 1); } } for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.isDead) { enemy.destroy(); enemies.splice(i, 1); continue; } if (enemy.hasReachedMeat) { LK.getSound('enemyReachesMeat').play(); LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); return; } // Check for collision between enemy and defenders for (var j = defenders.length - 1; j >= 0; j--) { var defender = defenders[j]; if (enemy.intersects(defender)) { // Remove the defender and decrement count var defenderType = null; if (defender instanceof Bird) { defenderType = 'bird'; } else if (defender instanceof Cat) { defenderType = 'cat'; } else if (defender instanceof Raccoon) { defenderType = 'raccoon'; } else if (defender instanceof Bear) { defenderType = 'bear'; } if (defenderType) { defenderCounts[defenderType]--; } defender.destroy(); defenders.splice(j, 1); updateDefenderButtonStates(); break; // Exit inner loop since defender is destroyed } } } // Update all defenders so they can shoot for (var i = 0; i < defenders.length; i++) { var defender = defenders[i]; defender.update(); } if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) { startNextWave(); } if (waveNumber > 10) { LK.showYouWin(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 1;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.isAtTarget = true;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var Defender = Container.expand(function () {
var self = Container.call(this);
self.range = 200;
self.fireRate = 60;
self.lastFireTime = 0;
self.damage = 1;
self.isPlaced = false;
self.canFire = function () {
return LK.ticks - self.lastFireTime > self.fireRate;
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead) {
continue;
}
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canFire()) {
return;
}
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
bullets.push(bullet);
game.addChild(bullet);
self.lastFireTime = LK.ticks;
LK.getSound('shoot').play();
};
self.update = function () {
if (!self.isPlaced) {
return;
}
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
return self;
});
var Raccoon = Defender.expand(function () {
var self = Defender.call(this);
var graphics = self.attachAsset('raccoon', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 350;
self.fireRate = 40;
self.damage = 2.5;
return self;
});
var Cat = Defender.expand(function () {
var self = Defender.call(this);
var graphics = self.attachAsset('cat', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 400;
self.fireRate = 50;
self.damage = 1.5;
return self;
});
var Bird = Defender.expand(function () {
var self = Defender.call(this);
var graphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 450;
self.fireRate = 80;
self.damage = 0.5;
return self;
});
var Bear = Defender.expand(function () {
var self = Defender.call(this);
var graphics = self.attachAsset('bear', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 300;
self.fireRate = 70;
self.damage = 4;
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.speed = 2;
self.health = 1;
self.maxHealth = 1;
self.isDead = false;
self.targetX = 0;
self.targetY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.isDead = true;
LK.setScore(LK.getScore() + 10);
money += self.coinReward || 1;
updateMoneyDisplay();
LK.getSound('enemyHit').play();
}
};
self.update = function () {
if (self.isDead) {
return;
}
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.hasReachedMeat = true;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var Worm = Enemy.expand(function () {
var self = Enemy.call(this);
var graphics = self.attachAsset('worm', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.health = 1;
self.maxHealth = 1;
self.coinReward = 1;
return self;
});
var Snake = Enemy.expand(function () {
var self = Enemy.call(this);
var graphics = self.attachAsset('snake', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.8;
self.health = 3;
self.maxHealth = 3;
self.coinReward = 3;
return self;
});
var Mouse = Enemy.expand(function () {
var self = Enemy.call(this);
var graphics = self.attachAsset('mouse', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2.5;
self.health = 2;
self.maxHealth = 2;
self.coinReward = 2;
return self;
});
var Ant = Enemy.expand(function () {
var self = Enemy.call(this);
var graphics = self.attachAsset('ant', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.maxHealth = 1;
self.coinReward = 1;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
var grassBackground = game.addChild(LK.getAsset('grass', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var meat = game.addChild(LK.getAsset('meat', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.5,
scaleY: 1.5
}));
var enemies = [];
var defenders = [];
var bullets = [];
var money = 7;
var selectedDefenderType = 'bird';
var rangeIndicator = null;
var showingRange = false;
var waveNumber = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var spawnTimer = 0;
var spawnInterval = 120;
var defenderCounts = {
'bird': 0,
'cat': 0,
'raccoon': 0,
'bear': 0
};
var defenderLimits = {
'bird': 5,
'cat': 4,
'raccoon': 3,
'bear': 2
};
var waveTxt = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0, 0);
waveTxt.x = 200;
waveTxt.y = 100;
LK.gui.topLeft.addChild(waveTxt);
var moneyTxt = new Text2('Money: 7', {
size: 50,
fill: 0xFFD700
});
moneyTxt.anchor.set(0, 0);
moneyTxt.x = 200;
moneyTxt.y = 160;
LK.gui.topLeft.addChild(moneyTxt);
var selectedDefenderType = 'bird';
var defenderTypes = ['bird', 'cat', 'raccoon', 'bear'];
var defenderCosts = {
'bird': 1,
'cat': 4,
'raccoon': 8,
'bear': 12
};
var defenderButtons = [];
function updateWave() {
waveTxt.setText('Wave: ' + waveNumber);
}
function updateMoneyDisplay() {
moneyTxt.setText('Money: ' + money);
updateDefenderButtonStates();
}
function createDefenderButtons() {
var buttonSize = 180;
var spacing = 60;
var totalWidth = defenderTypes.length * buttonSize + (defenderTypes.length - 1) * spacing;
var startX = (2048 - totalWidth) / 2;
var y = 2732 - buttonSize - 50;
for (var i = 0; i < defenderTypes.length; i++) {
var defenderType = defenderTypes[i];
var cost = defenderCosts[defenderType];
// Create button container
var button = new Container();
button.defenderType = defenderType;
button.cost = cost;
button.x = startX + i * (buttonSize + spacing);
button.y = y;
button.width = buttonSize;
button.height = buttonSize + 60; // Extra height for text
// Background removed for cleaner look
// Add defender image
var defenderImage = LK.getAsset(defenderType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
button.addChild(defenderImage);
// Add cost text
var costText = new Text2('$' + cost, {
size: 60,
fill: 0xFFD700
});
costText.anchor.set(0.5, 0);
costText.x = 0;
costText.y = buttonSize / 2 + 15;
button.addChild(costText);
button.costText = costText;
// Add count/limit text
var countText = new Text2('0/' + defenderLimits[defenderType], {
size: 40,
fill: 0xFFFFFF
});
countText.anchor.set(0.5, 0);
countText.x = 0;
countText.y = buttonSize / 2 + 75;
button.addChild(countText);
button.countText = countText;
// Add selection indicator
var selectionIndicator = LK.getAsset('rangeIndicator', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7,
alpha: 0.6
});
selectionIndicator.visible = false;
button.addChild(selectionIndicator);
button.selectionIndicator = selectionIndicator;
defenderButtons.push(button);
game.addChild(button);
}
updateDefenderButtonStates();
}
function updateDefenderButtonStates() {
for (var i = 0; i < defenderButtons.length; i++) {
var button = defenderButtons[i];
var canAfford = money >= button.cost;
var isSelected = button.defenderType === selectedDefenderType;
var hasReachedLimit = defenderCounts[button.defenderType] >= defenderLimits[button.defenderType];
// Update affordability colors and limits
if (hasReachedLimit) {
button.costText.fill = 0x888888; // Gray for limit reached
button.alpha = 0.3;
} else {
button.costText.fill = canAfford ? 0xFFD700 : 0xFF0000;
button.alpha = canAfford ? 1.0 : 0.5;
}
// Update selection indicator
button.selectionIndicator.visible = isSelected;
// Update count text
button.countText.setText(defenderCounts[button.defenderType] + '/' + defenderLimits[button.defenderType]);
}
}
createDefenderButtons();
var snakeSpawnedThisWave = false;
function spawnEnemy() {
var enemyTypes = [Worm, Ant, Mouse];
if (waveNumber < 5) {
// Before wave 5: Snake can spawn once per wave
if (!snakeSpawnedThisWave && Math.random() < 0.2) {
enemyTypes.push(Snake);
}
} else {
// Wave 5 and after: Snake can spawn unlimited
enemyTypes.push(Snake);
}
var EnemyClass = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
if (EnemyClass === Snake && waveNumber < 5) {
snakeSpawnedThisWave = true;
}
var enemy = new EnemyClass();
// Boost enemy health after wave 5
if (waveNumber > 5) {
var healthBoost = Math.floor((waveNumber - 5) / 2) + 1; // +1 HP every 2 waves after wave 5
enemy.health += healthBoost;
enemy.maxHealth += healthBoost;
}
var spawnSides = [{
x: 0,
y: Math.random() * 2732,
flipX: 1 // Left side - face right (flip)
}, {
x: 2048,
y: Math.random() * 2732,
flipX: 0 // Right side - face left (no flip)
}, {
x: Math.random() * 2048,
y: 0,
flipX: 0 // Top side - no flip
}, {
x: Math.random() * 2048,
y: 2732,
flipX: 0 // Bottom side - no flip
}];
var spawn = spawnSides[Math.floor(Math.random() * spawnSides.length)];
enemy.x = spawn.x;
enemy.y = spawn.y;
enemy.targetX = meat.x;
enemy.targetY = meat.y;
// Apply horizontal flip based on spawn side and position relative to meat
if (spawn.flipX === 1) {
enemy.scaleX = -1;
} else if (spawn.x === 0 || spawn.x === 2048) {
// Left or right spawn - already handled above
} else {
// Top or bottom spawn - face towards meat horizontally
if (enemy.x > meat.x) {
// Enemy is to the right of meat - face left
enemy.scaleX = -1;
} else {
// Enemy is to the left of meat - face right
enemy.scaleX = 1;
}
}
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function startNextWave() {
waveNumber++;
enemiesSpawned = 0;
enemiesPerWave = Math.min((5 + Math.floor(waveNumber / 2)) * 2, 30);
spawnInterval = Math.max(60 - waveNumber * 5, 20);
snakeSpawnedThisWave = false;
updateWave();
}
function showRangeIndicator(x, y) {
if (rangeIndicator) {
rangeIndicator.destroy();
}
rangeIndicator = LK.getAsset('rangeIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.3
});
var range = 450;
if (selectedDefenderType === 'bird') {
range = 450;
} else if (selectedDefenderType === 'bear') {
range = 300;
} else if (selectedDefenderType === 'cat') {
range = 400;
} else if (selectedDefenderType === 'raccoon') {
range = 350;
}
rangeIndicator.width = range * 2;
rangeIndicator.height = range * 2;
game.addChild(rangeIndicator);
showingRange = true;
}
function hideRangeIndicator() {
if (rangeIndicator) {
rangeIndicator.destroy();
rangeIndicator = null;
}
showingRange = false;
}
game.down = function (x, y, obj) {
// Check if clicked on a defender button
for (var i = 0; i < defenderButtons.length; i++) {
var button = defenderButtons[i];
var buttonCenterX = button.x;
var buttonCenterY = button.y;
var buttonWidth = 180;
var buttonHeight = 240; // Including text area
// Check if click is within button bounds
if (x >= buttonCenterX - buttonWidth / 2 && x <= buttonCenterX + buttonWidth / 2 && y >= buttonCenterY - buttonHeight / 2 && y <= buttonCenterY + buttonHeight / 2) {
// Check if player can afford this defender and hasn't reached limit
if (money >= button.cost && defenderCounts[button.defenderType] < defenderLimits[button.defenderType]) {
selectedDefenderType = button.defenderType;
updateDefenderButtonStates();
}
return;
}
}
// Check if valid placement area (not too close to edges or meat)
if (y < 150 || y > 2732 - 200) {
return;
} // Too close to top or bottom
if (x < 100 || x > 2048 - 100) {
return;
} // Too close to sides
// Check if too close to meat
var distanceToMeat = Math.sqrt((x - meat.x) * (x - meat.x) + (y - meat.y) * (y - meat.y));
if (distanceToMeat < 150) {
return;
} // Too close to meat
// Check if space is already occupied by another defender
var spaceTaken = false;
for (var i = 0; i < defenders.length; i++) {
var defender = defenders[i];
var distance = Math.sqrt((x - defender.x) * (x - defender.x) + (y - defender.y) * (y - defender.y));
if (distance < 100) {
spaceTaken = true;
break;
}
}
if (spaceTaken) {
return;
}
// Check if player has enough money and hasn't reached limit
var cost = defenderCosts[selectedDefenderType];
if (money < cost) {
return;
}
if (defenderCounts[selectedDefenderType] >= defenderLimits[selectedDefenderType]) {
return;
}
// Place defender
var DefenderClass;
if (selectedDefenderType === 'bird') {
DefenderClass = Bird;
} else if (selectedDefenderType === 'bear') {
DefenderClass = Bear;
} else if (selectedDefenderType === 'cat') {
DefenderClass = Cat;
} else if (selectedDefenderType === 'raccoon') {
DefenderClass = Raccoon;
}
var newDefender = new DefenderClass();
newDefender.x = x;
newDefender.y = y;
newDefender.isPlaced = true;
// Face defender towards meat
if (x > meat.x) {
// Right side - face left (flip)
newDefender.scaleX = -1;
} else {
// Left side - face right (no flip)
newDefender.scaleX = 1;
}
defenders.push(newDefender);
game.addChild(newDefender);
// Deduct money and increment count
money -= cost;
defenderCounts[selectedDefenderType]++;
updateMoneyDisplay();
// Show range indicator briefly
showRangeIndicator(x, y);
LK.setTimeout(function () {
hideRangeIndicator();
}, 1000);
};
game.update = function () {
if (enemiesSpawned < enemiesPerWave) {
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnEnemy();
spawnTimer = 0;
}
}
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.isAtTarget) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
var hitEnemy = false;
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
if (enemy.isDead) {
continue;
}
if (bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
hitEnemy = true;
break;
}
}
if (hitEnemy) {
bullet.destroy();
bullets.splice(i, 1);
}
}
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.isDead) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
if (enemy.hasReachedMeat) {
LK.getSound('enemyReachesMeat').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
}
// Check for collision between enemy and defenders
for (var j = defenders.length - 1; j >= 0; j--) {
var defender = defenders[j];
if (enemy.intersects(defender)) {
// Remove the defender and decrement count
var defenderType = null;
if (defender instanceof Bird) {
defenderType = 'bird';
} else if (defender instanceof Cat) {
defenderType = 'cat';
} else if (defender instanceof Raccoon) {
defenderType = 'raccoon';
} else if (defender instanceof Bear) {
defenderType = 'bear';
}
if (defenderType) {
defenderCounts[defenderType]--;
}
defender.destroy();
defenders.splice(j, 1);
updateDefenderButtonStates();
break; // Exit inner loop since defender is destroyed
}
}
}
// Update all defenders so they can shoot
for (var i = 0; i < defenders.length; i++) {
var defender = defenders[i];
defender.update();
}
if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
startNextWave();
}
if (waveNumber > 10) {
LK.showYouWin();
}
};
ant without background. In-Game asset. 2d. High contrast. No shadows
bear no background. In-Game asset. 2d. High contrast. No shadows
bird no background. In-Game asset. 2d. High contrast. No shadows
cat no background. In-Game asset. 2d. High contrast. No shadows
meat no background. In-Game asset. 2d. High contrast. No shadows
mouse. In-Game asset. 2d. High contrast. No shadows
raccoon no background. In-Game asset. 2d. High contrast. No shadows
snake no background. In-Game asset. 2d. High contrast. No shadows
worm no background. In-Game asset. 2d. High contrast. No shadows
A background with grass details, shot from above, and 4 dirt roads towards the middle, merging from right to left, bottom to top in the middle. In-Game asset. 2d. High contrast. No shadows