/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AmenoEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('amenoGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 150;
self.speed = 1.2;
self.pathIndex = 0;
self.reward = 20;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.potionTimer = 0;
self.potionRate = 240; // 4 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff513a, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC; // Purple tint for stun
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.throwPotion = function () {
var nearestTower = findNearestTower(self.x, self.y, 200);
if (nearestTower) {
var potion = new PoisonPotion();
potion.x = self.x;
potion.y = self.y;
potion.target = nearestTower;
potions.push(potion);
game.addChild(potion);
LK.getSound('potion').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0xFFFFFF;
}
}
// Handle stun timer
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0xFFFFFF;
}
}
// Potion throwing ability
self.potionTimer++;
if (self.potionTimer >= self.potionRate && !self.frozen && !self.stunned) {
self.throwPotion();
self.potionTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var AntiVirusGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('antiVirusGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x00FF00; // Green anti-virus color
self.range = 160;
self.cleanseRate = 120; // 2 seconds at 60fps
self.cleanseTimer = 0;
self.cost = 150;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.cleanseTimer++;
if (self.cleanseTimer >= self.cleanseRate) {
// Find all towers in range that are disabled
var towersInRange = [];
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (!tower.destroyed && tower.disabled) {
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
towersInRange.push(tower);
}
}
}
// Cleanse all disabled towers in range
for (var j = 0; j < towersInRange.length; j++) {
var tower = towersInRange[j];
tower.disabled = false;
tower.disableTimer = 0;
// Visual cleansing effect
LK.effects.flashObject(tower, 0x00FF00, 800);
tween(tower, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
if (towersInRange.length > 0) {
// Visual effect for anti-virus goose
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0x00FF00
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.getSound('antiVirus').play();
}
self.cleanseTimer = 0;
}
}
};
return self;
});
var BlueSunEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('blueSunGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x4169E1; // Royal blue color
self.health = 200;
self.maxHealth = 200;
self.speed = 1.3;
self.pathIndex = 0;
self.reward = 35;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.swordTimer = 0;
self.swordRate = 150; // 2.5 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff1e00, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC;
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.swordSlash = function () {
var nearestTower = findNearestTower(self.x, self.y, 180);
if (nearestTower) {
var slash = new BlueSunSlash();
slash.x = self.x;
slash.y = self.y;
slash.target = nearestTower;
potions.push(slash);
game.addChild(slash);
LK.getSound('blueSunSword').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0x4169E1;
}
}
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0x4169E1;
}
}
self.swordTimer++;
if (self.swordTimer >= self.swordRate && !self.frozen && !self.stunned) {
self.swordSlash();
self.swordTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var BlueSunSlash = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x4169E1; // Royal blue color
self.speed = 8;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Blue sun slash disables tower for 4 seconds
self.target.disabled = true;
self.target.disableTimer = 240; // 4 seconds at 60fps
LK.effects.flashObject(self.target, 0x4169E1, 700);
// Apply world modifier weakness
if (self.target.worldModifier) {
self.target.worldModifier = 0.5; // Weakened in wrong world
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.worldModifier = 1.0;
}
}, 2500);
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var CanadianGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('canadianGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 150;
self.damage = 25;
self.fireRate = 60; // 1 second at 60fps
self.fireTimer = 0;
self.cost = 50;
self.disabled = false;
self.disableTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = self.damage;
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('honk').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var EmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('emdenGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 1.5;
self.pathIndex = 0;
self.reward = 10;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC; // Purple tint for stun
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
// Charm state variables
self.charmed = false;
self.charmTimer = 0;
self.originalSpeed = self.speed;
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0xFFFFFF;
}
}
// Handle stun timer
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0xFFFFFF;
}
}
// Handle charm timer - charmed enemies move backwards
if (self.charmed && self.charmTimer > 0) {
self.charmTimer--;
if (self.charmTimer <= 0) {
self.charmed = false;
self.speed = self.originalSpeed;
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
}
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target;
// If charmed, move towards previous path point (backwards)
if (self.charmed && self.pathIndex > 0) {
target = pathPoints[self.pathIndex - 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex--;
if (self.pathIndex <= 0) {
// If charmed enemy reaches start, remove it and give bonus resources
resources += self.reward * 2;
updateResourceDisplay();
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * Math.abs(self.speed);
self.y += Math.sin(angle) * Math.abs(self.speed);
}
} else {
// Normal forward movement
target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
}
};
return self;
});
var FakeEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('emdenGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden tint to show it's fake
self.charmRange = 100;
self.lifetime = 600; // 10 seconds at 60fps
self.timer = 0;
self.charmedEnemies = [];
self.update = function () {
self.timer++;
// Pulsing effect to show it's fake
graphics.alpha = 0.7 + Math.sin(self.timer * 0.1) * 0.3;
graphics.rotation += 0.02;
// Find nearby real enemies to charm
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && self.charmedEnemies.indexOf(enemy) === -1) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.charmRange) {
// Charm the enemy - make it move backwards on path
self.charmedEnemies.push(enemy);
enemy.charmed = true;
enemy.charmTimer = 300; // 5 seconds charm duration
enemy.originalSpeed = enemy.speed;
enemy.speed = -enemy.speed * 0.5; // Move backwards slower
// Visual charm effect
tween(enemy, {
tint: 0xFFB6C1
}, {
duration: 500,
easing: tween.easeOut
});
LK.effects.flashObject(enemy, 0xFFD700, 800);
}
}
}
// Remove charm from enemies over time
for (var j = self.charmedEnemies.length - 1; j >= 0; j--) {
var charmedEnemy = self.charmedEnemies[j];
if (charmedEnemy.destroyed || !charmedEnemy.charmed) {
self.charmedEnemies.splice(j, 1);
} else if (charmedEnemy.charmTimer <= 0) {
charmedEnemy.charmed = false;
charmedEnemy.speed = charmedEnemy.originalSpeed;
charmedEnemy.charmTimer = 0;
tween(charmedEnemy, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
self.charmedEnemies.splice(j, 1);
} else {
charmedEnemy.charmTimer--;
}
}
// Destroy fake goose after lifetime
if (self.timer >= self.lifetime) {
// Remove charm from all enemies when fake goose dies
for (var k = 0; k < self.charmedEnemies.length; k++) {
var enemy = self.charmedEnemies[k];
if (!enemy.destroyed) {
enemy.charmed = false;
enemy.speed = enemy.originalSpeed;
enemy.charmTimer = 0;
tween(enemy, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
}
}
self.destroy();
}
};
return self;
});
var FireMagic = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFF4500; // Orange-red fire color
self.speed = 6;
self.target = null;
self.damage = 45;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
// Primary fire damage
self.target.takeDamage(self.damage);
LK.effects.flashObject(self.target, 0xFF4500, 400);
// Burning damage over time - 6 ticks of burn damage
var burnDamage = 12;
var burnTicks = 6;
for (var bt = 1; bt <= burnTicks; bt++) {
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.takeDamage(burnDamage);
LK.effects.flashObject(self.target, 0xFF6600, 150);
}
}, bt * 500);
}
// Fire spreads to nearby enemies with area damage
var nearbyEnemies = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && enemy !== self.target) {
var edx = enemy.x - self.target.x;
var edy = enemy.y - self.target.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 90) {
nearbyEnemies.push(enemy);
}
}
}
for (var j = 0; j < nearbyEnemies.length; j++) {
var splashDamage = Math.floor(self.damage * 0.6);
nearbyEnemies[j].takeDamage(splashDamage);
LK.effects.flashObject(nearbyEnemies[j], 0xFF4500, 300);
// Reduced burning effect on nearby enemies
var nearbyBurnDamage = 8;
var nearbyBurnTicks = 3;
for (var nbt = 1; nbt <= nearbyBurnTicks; nbt++) {
LK.setTimeout(function () {
if (nearbyEnemies[j] && !nearbyEnemies[j].destroyed) {
nearbyEnemies[j].takeDamage(nearbyBurnDamage);
LK.effects.flashObject(nearbyEnemies[j], 0xFF6600, 100);
}
}, nbt * 600);
}
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var HammerGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hammerGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9932CC; // Electro purple color
self.range = 100;
self.stunDuration = 180; // 3 seconds at 60fps
self.fireRate = 90; // 1.5 seconds at 60fps
self.fireTimer = 0;
self.cost = 120;
self.disabled = false;
self.disableTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target && !target.stunned) {
// Hammer swing animation
tween(graphics, {
rotation: graphics.rotation + Math.PI / 4
}, {
duration: 150,
easing: tween.easeOut
});
tween(graphics, {
rotation: graphics.rotation
}, {
duration: 150,
easing: tween.easeIn
});
// Stun the enemy
target.stun(self.stunDuration);
LK.getSound('hammer').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var HydroArrow = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x0066CC; // Blue hydro color
self.speed = 7;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Disable tower for 4 seconds and deal damage
self.target.disabled = true;
self.target.disableTimer = 240; // 4 seconds at 60fps
LK.effects.flashObject(self.target, 0x0066CC, 600);
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var HydroArrowEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hydroGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x0066CC; // Blue hydro color
self.health = 150;
self.maxHealth = 150;
self.speed = 1.0;
self.pathIndex = 0;
self.reward = 30;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.arrowTimer = 0;
self.arrowRate = 180; // 3 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff1e00, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC; // Purple tint for stun
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.shootArrow = function () {
var nearestTower = findNearestTower(self.x, self.y, 250);
if (nearestTower) {
var arrow = new HydroArrow();
arrow.x = self.x;
arrow.y = self.y;
arrow.target = nearestTower;
potions.push(arrow);
game.addChild(arrow);
LK.getSound('hydroArrow').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0x0066CC; // Return to blue hydro color
}
}
// Handle stun timer
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0x0066CC; // Return to blue hydro color
}
}
// Hydro arrow shooting ability
self.arrowTimer++;
if (self.arrowTimer >= self.arrowRate && !self.frozen && !self.stunned) {
self.shootArrow();
self.arrowTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var IceEffect = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('iceEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
self.duration = 180; // 3 seconds at 60fps
self.timer = 0;
self.update = function () {
self.timer++;
graphics.rotation += 0.1;
if (self.timer >= self.duration) {
self.destroy();
}
};
return self;
});
var IceGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('iceGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 120;
self.freezeDuration = 120; // 2 seconds at 60fps
self.fireRate = 180; // 3 seconds at 60fps
self.fireTimer = 0;
self.cost = 80;
self.disabled = false;
self.disableTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target && !target.frozen) {
target.freeze(self.freezeDuration);
LK.getSound('freeze').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var MoonMagic = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9370DB; // Purple moonlight color
self.speed = 7;
self.target = null;
self.damage = 40;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
LK.effects.flashObject(self.target, 0x9370DB, 400);
// Moonlight magic spreads to nearby enemies
var nearbyEnemies = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && enemy !== self.target) {
var edx = enemy.x - self.target.x;
var edy = enemy.y - self.target.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 80) {
nearbyEnemies.push(enemy);
}
}
}
for (var j = 0; j < nearbyEnemies.length; j++) {
nearbyEnemies[j].takeDamage(Math.floor(self.damage * 0.5));
LK.effects.flashObject(nearbyEnemies[j], 0x9370DB, 200);
}
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroy();
}
};
return self;
});
var MoonlightSleeper = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('moonlightSleeper', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9370DB; // Purple moonlight color
self.range = 200;
self.damage = 40;
self.fireRate = 90; // Slower but powerful magic
self.fireTimer = 0;
self.cost = 120;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0; // Damage modifier based on world
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new MoonMagic();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('moonMagic').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var PieProjectile = Container.expand(function () {
var self = Container.call(this);
self.pieType = 'apple'; // 'apple', 'cherry', 'blueberry'
self.speed = 6;
self.target = null;
self.damage = 30;
self.destroyed = false;
// Use appropriate pie asset based on type
var assetId = 'applePie';
if (self.pieType === 'cherry') assetId = 'cherryPie';
if (self.pieType === 'blueberry') assetId = 'blueberryPie';
var graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
// Apply pie-specific effects on enemies
if (self.pieType === 'apple') {
// Apple pie: slow enemy for 3 seconds
self.target.takeDamage(self.damage);
if (!self.target.frozen && !self.target.stunned) {
var originalSpeed = self.target.speed;
self.target.speed = originalSpeed * 0.5;
tween(self.target, {
tint: 0x90EE90
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.target, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.speed = originalSpeed;
}
}, 3000);
}
} else if (self.pieType === 'cherry') {
// Cherry pie: burning damage over time
self.target.takeDamage(self.damage);
var burnDamage = 8;
var burnTicks = 5;
for (var bt = 1; bt <= burnTicks; bt++) {
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.takeDamage(burnDamage);
LK.effects.flashObject(self.target, 0xFF6B6B, 200);
}
}, bt * 600);
}
} else if (self.pieType === 'blueberry') {
// Blueberry pie: chance to freeze enemy
self.target.takeDamage(self.damage);
if (Math.random() < 0.4 && !self.target.frozen) {
self.target.freeze(120); // 2 seconds freeze
}
}
// Pie splash effect with flavor-specific colors
var splashColor = 0x90EE90;
if (self.pieType === 'cherry') splashColor = 0xFF6B6B;
if (self.pieType === 'blueberry') splashColor = 0x4169E1;
LK.effects.flashObject(self.target, splashColor, 400);
// Area damage to nearby enemies
var nearbyEnemies = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && enemy !== self.target) {
var edx = enemy.x - self.target.x;
var edy = enemy.y - self.target.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 60) {
nearbyEnemies.push(enemy);
}
}
}
for (var j = 0; j < nearbyEnemies.length; j++) {
var splashDamage = Math.floor(self.damage * 0.4);
nearbyEnemies[j].takeDamage(splashDamage);
LK.effects.flashObject(nearbyEnemies[j], splashColor, 200);
// Apply reduced pie effects to nearby enemies
if (self.pieType === 'apple' && !nearbyEnemies[j].frozen && !nearbyEnemies[j].stunned) {
var enemySpeed = nearbyEnemies[j].speed;
nearbyEnemies[j].speed = enemySpeed * 0.7;
LK.setTimeout(function () {
if (nearbyEnemies[j] && !nearbyEnemies[j].destroyed) {
nearbyEnemies[j].speed = enemySpeed;
}
}, 1500);
} else if (self.pieType === 'blueberry' && Math.random() < 0.2 && !nearbyEnemies[j].frozen) {
nearbyEnemies[j].freeze(60);
}
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var PieShooterGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pieShooterGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFB6C1; // Light pink color for pie shooter
self.range = 160;
self.damage = 30;
self.fireRate = 75; // Moderate shooting speed
self.fireTimer = 0;
self.cost = 110;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
self.healRange = 120;
self.healRate = 180; // 3 seconds at 60fps
self.healTimer = 0;
self.healAmount = 15;
self.lastPieType = 'apple'; // Track last pie type for healing effects
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
self.healTimer++;
// Shooting pies at enemies
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new PieProjectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
// Randomly select pie type
var pieTypes = ['apple', 'cherry', 'blueberry'];
projectile.pieType = pieTypes[Math.floor(Math.random() * pieTypes.length)];
self.lastPieType = projectile.pieType; // Track for healing effects
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('pieShot').play();
self.fireTimer = 0;
}
}
// Healing nearby Canadian Geese towers
if (self.healTimer >= self.healRate) {
var towersInRange = [];
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (!tower.destroyed && (tower.constructor === CanadianGoose || tower.constructor === PieShooterGoose)) {
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.healRange && tower !== self) {
towersInRange.push(tower);
}
}
}
// Heal all Canadian Geese in range with different pie healing effects
for (var j = 0; j < towersInRange.length; j++) {
var tower = towersInRange[j];
// Determine healing type based on what pie flavor was last shot
var currentPieType = 'apple'; // default
if (self.lastPieType) {
currentPieType = self.lastPieType;
}
// Apply different healing effects based on pie flavor
if (currentPieType === 'apple') {
// Apple healing: boost fire rate temporarily
if (tower.fireRate && !tower.appleBoosted) {
tower.appleBoosted = true;
var originalFireRate = tower.fireRate;
tower.fireRate = Math.floor(originalFireRate * 0.7); // 30% faster
tween(tower, {
tint: 0x90EE90
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (tower && !tower.destroyed) {
tower.fireRate = originalFireRate;
tower.appleBoosted = false;
}
}, 5000);
}
} else if (currentPieType === 'cherry') {
// Cherry healing: boost damage temporarily
if (tower.damage && !tower.cherryBoosted) {
tower.cherryBoosted = true;
var originalDamage = tower.damage;
tower.damage = Math.floor(originalDamage * 1.3); // 30% more damage
tween(tower, {
tint: 0xFF6B6B
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (tower && !tower.destroyed) {
tower.damage = originalDamage;
tower.cherryBoosted = false;
}
}, 5000);
}
} else if (currentPieType === 'blueberry') {
// Blueberry healing: boost range temporarily
if (tower.range && !tower.blueberryBoosted) {
tower.blueberryBoosted = true;
var originalRange = tower.range;
tower.range = Math.floor(originalRange * 1.25); // 25% more range
tween(tower, {
tint: 0x4169E1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (tower && !tower.destroyed) {
tower.range = originalRange;
tower.blueberryBoosted = false;
}
}, 5000);
}
}
// Base visual healing effect
LK.effects.flashObject(tower, 0x00FF7F, 600);
tween(tower, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeIn
});
}
});
}
if (towersInRange.length > 0) {
// Visual effect for pie shooter goose healing
tween(graphics, {
tint: 0x00FF7F
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFB6C1
}, {
duration: 400,
easing: tween.easeIn
});
}
});
}
self.healTimer = 0;
}
}
};
return self;
});
var PoisonPotion = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9932CC; // Purple color for poison
self.speed = 6;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Disable tower for 3 seconds
self.target.disabled = true;
self.target.disableTimer = 180; // 3 seconds at 60fps
LK.effects.flashObject(self.target, 0x9932CC, 500);
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.target = null;
self.damage = 25;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroy();
}
};
return self;
});
var PyroCatalystGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pyroCatalystGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFF4500; // Orange-red fire color
self.range = 170;
self.damage = 45;
self.fireRate = 80; // Moderate casting speed
self.fireTimer = 0;
self.cost = 140;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
// Fire magic charging effect
self.chargingEffect = false;
self.chargeTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
// Charging effect before casting
if (self.fireTimer >= self.fireRate - 20 && !self.chargingEffect) {
self.chargingEffect = true;
tween(graphics, {
tint: 0xFFFF00,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut
});
}
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new FireMagic();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('fireMagic').play();
// Fire casting animation
tween(graphics, {
rotation: graphics.rotation + Math.PI / 6
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
rotation: graphics.rotation,
tint: 0xFF4500,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
self.fireTimer = 0;
self.chargingEffect = false;
}
}
}
};
return self;
});
var RedMoonEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('redMoonGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x8B0000; // Dark red color
self.health = 180;
self.maxHealth = 180;
self.speed = 1.1;
self.pathIndex = 0;
self.reward = 40;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.magicTimer = 0;
self.magicRate = 200; // 3.3 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff1e00, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC;
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.castRedMoonMagic = function () {
var nearestTower = findNearestTower(self.x, self.y, 220);
if (nearestTower) {
var magic = new RedMoonMagic();
magic.x = self.x;
magic.y = self.y;
magic.target = nearestTower;
potions.push(magic);
game.addChild(magic);
LK.getSound('redMoonMagic').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0x8B0000;
}
}
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0x8B0000;
}
}
self.magicTimer++;
if (self.magicTimer >= self.magicRate && !self.frozen && !self.stunned) {
self.castRedMoonMagic();
self.magicTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var RedMoonMagic = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x8B0000; // Dark red color
self.speed = 6;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Red moon magic disables tower for 5 seconds and deals damage
self.target.disabled = true;
self.target.disableTimer = 300; // 5 seconds at 60fps
LK.effects.flashObject(self.target, 0x8B0000, 800);
// Apply world modifier weakness
if (self.target.worldModifier) {
self.target.worldModifier = 0.5; // Weakened in wrong world
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.worldModifier = 1.0;
}
}, 3000);
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var StunEffect = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('stunEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
graphics.tint = 0x9932CC; // Purple color for stun effect
self.duration = 120; // 2 seconds at 60fps
self.timer = 0;
self.update = function () {
self.timer++;
graphics.rotation += 0.15;
graphics.scaleX = 1 + Math.sin(self.timer * 0.2) * 0.1;
graphics.scaleY = 1 + Math.sin(self.timer * 0.2) * 0.1;
if (self.timer >= self.duration) {
self.destroy();
}
};
return self;
});
var SunArrow = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden sun color
self.speed = 9;
self.target = null;
self.damage = 35;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
LK.effects.flashObject(self.target, 0xFFD700, 300);
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroy();
}
};
return self;
});
var SunShooter = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('sunShooter', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden sun color
self.range = 180;
self.damage = 35;
self.fireRate = 45; // Fast shooting
self.fireTimer = 0;
self.cost = 100;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0; // Damage modifier based on world
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new SunArrow();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('sunArrow').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var TreasureHoarderGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('treasureHoarderGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden treasure color
self.range = 200;
self.fakeGooseRate = 480; // 8 seconds at 60fps
self.fakeGooseTimer = 0;
self.cost = 200;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
// Treasure collection ability - gains resources over time
self.treasureRate = 300; // 5 seconds at 60fps
self.treasureTimer = 0;
self.treasureAmount = 15;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fakeGooseTimer++;
self.treasureTimer++;
// Treasure hoarding - generate resources
if (self.treasureTimer >= self.treasureRate) {
resources += self.treasureAmount;
updateResourceDisplay();
// Treasure collection visual effect
tween(graphics, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FF00
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFD700
}, {
duration: 200,
easing: tween.easeIn
});
}
});
self.treasureTimer = 0;
}
// Create fake Emden goose to charm enemies
if (self.fakeGooseTimer >= self.fakeGooseRate) {
var nearestEnemy = findNearestEnemy(self.x, self.y, self.range);
if (nearestEnemy) {
var fakeGoose = new FakeEmdenGoose();
// Position fake goose strategically near enemy path
var targetPathPoint = pathPoints[Math.min(nearestEnemy.pathIndex + 1, pathPoints.length - 1)];
fakeGoose.x = targetPathPoint.x + (Math.random() - 0.5) * 120;
fakeGoose.y = targetPathPoint.y + (Math.random() - 0.5) * 120;
game.addChild(fakeGoose);
// Summoning visual effect
tween(graphics, {
rotation: graphics.rotation + Math.PI / 3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
rotation: graphics.rotation
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(self, 0xFFD700, 600);
LK.getSound('charm').play();
}
self.fakeGooseTimer = 0;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Game state variables
var gameState = 'menu'; // 'menu', 'playing', 'paused'
var mainMenu;
var gameInitialized = false;
// Game variables
var resources = 999999;
var lives = 20;
var wave = 1;
var enemies = [];
var towers = [];
var projectiles = [];
var potions = [];
var waveTimer = 0;
var waveDelay = 300; // 5 seconds between waves
var enemiesPerWave = 5;
var selectedTowerType = 'canadian';
var currentWorld = 'normal'; // 'normal', 'sun', 'moon'
var episode = 1;
// UI Elements - declare globally to be accessible
var resourceTxt;
var livesTxt;
var waveTxt;
var canadianBtn;
var iceBtn;
var hammerBtn;
var sunBtn;
var moonBtn;
var antiVirusBtn;
var pieShooterBtn;
var pyroCatalystBtn;
var treasureHoarderBtn;
// Path points for enemies to follow
var pathPoints = [{
x: 0,
y: 400
}, {
x: 300,
y: 400
}, {
x: 300,
y: 800
}, {
x: 700,
y: 800
}, {
x: 700,
y: 1200
}, {
x: 1100,
y: 1200
}, {
x: 1100,
y: 1600
}, {
x: 1500,
y: 1600
}, {
x: 1500,
y: 2000
}, {
x: 2048,
y: 2000
}];
// Initialize Main Menu
function createMainMenu() {
mainMenu = new Container();
// Title
var titleText = new Text2('Canadian Geese Tower Defense', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
mainMenu.addChild(titleText);
// Subtitle
var subtitleText = new Text2('Defend Against the Emden Invasion!', {
size: 80,
fill: 0xFFD700
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 550;
mainMenu.addChild(subtitleText);
// Play Button
var playButton = new Text2('START GAME', {
size: 100,
fill: 0x00FF00
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 1024;
playButton.y = 800;
playButton.down = function () {
startGame();
};
mainMenu.addChild(playButton);
// Instructions
var instructText = new Text2('Tap to place towers and defend your treasure!', {
size: 60,
fill: 0xCCCCCC
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1000;
mainMenu.addChild(instructText);
// Episode info
var episodeText = new Text2('3 Episodes: Normal → Sun World → Moon World', {
size: 50,
fill: 0x87CEEB
});
episodeText.anchor.set(0.5, 0.5);
episodeText.x = 1024;
episodeText.y = 1150;
mainMenu.addChild(episodeText);
// Animation for title
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
yoyo: true,
repeat: -1
});
// Animation for play button
tween(playButton, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
yoyo: true,
repeat: -1
});
game.addChild(mainMenu);
}
function startGame() {
if (mainMenu) {
mainMenu.destroy();
mainMenu = null;
}
gameState = 'playing';
if (!gameInitialized) {
initializeGame();
gameInitialized = true;
}
}
function initializeGame() {
// Create path visuals
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var segments = Math.ceil(Math.sqrt((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / 100);
for (var j = 0; j < segments; j++) {
var t = j / segments;
var pathSegment = game.addChild(LK.getAsset('path', {
anchorX: 0.5,
anchorY: 0.5,
x: start.x + (end.x - start.x) * t,
y: start.y + (end.y - start.y) * t,
alpha: 0.3
}));
}
}
// Create treasure at end
var treasure = game.addChild(LK.getAsset('treasure', {
anchorX: 0.5,
anchorY: 0.5,
x: pathPoints[pathPoints.length - 1].x - 100,
y: pathPoints[pathPoints.length - 1].y
}));
// UI Elements
resourceTxt = new Text2('Resources: ' + resources, {
size: 60,
fill: 0xFFFFFF
});
resourceTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(resourceTxt);
resourceTxt.x = -300;
resourceTxt.y = 50;
livesTxt = new Text2('Lives: ' + lives, {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(livesTxt);
livesTxt.x = -300;
livesTxt.y = 120;
waveTxt = new Text2('Wave: ' + wave, {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(waveTxt);
waveTxt.x = 120;
waveTxt.y = 50;
// Tower selection buttons
canadianBtn = new Text2('Canadian Goose (50)', {
size: 50,
fill: 0x8B4513
});
canadianBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(canadianBtn);
canadianBtn.y = -200;
canadianBtn.x = -200;
iceBtn = new Text2('Ice Goose (80)', {
size: 50,
fill: 0x87CEEB
});
iceBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(iceBtn);
iceBtn.y = -200;
iceBtn.x = 0;
hammerBtn = new Text2('Hammer Goose (120)', {
size: 50,
fill: 0x9932CC
});
hammerBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(hammerBtn);
hammerBtn.y = -200;
hammerBtn.x = 200;
sunBtn = new Text2('Sun Shooter (100)', {
size: 50,
fill: 0xFFD700
});
sunBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(sunBtn);
sunBtn.y = -140;
sunBtn.x = -200;
moonBtn = new Text2('Moonlight Sleeper (120)', {
size: 50,
fill: 0x9370DB
});
moonBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(moonBtn);
moonBtn.y = -140;
moonBtn.x = 200;
antiVirusBtn = new Text2('Anti Virus (150)', {
size: 50,
fill: 0x00FF00
});
antiVirusBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(antiVirusBtn);
antiVirusBtn.y = -80;
antiVirusBtn.x = -100;
pieShooterBtn = new Text2('Pie Shooter (110)', {
size: 50,
fill: 0xFFB6C1
});
pieShooterBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(pieShooterBtn);
pieShooterBtn.y = -80;
pieShooterBtn.x = 50;
pyroCatalystBtn = new Text2('Pyro Catalyst (140)', {
size: 50,
fill: 0xFF4500
});
pyroCatalystBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(pyroCatalystBtn);
pyroCatalystBtn.y = -80;
pyroCatalystBtn.x = 150;
treasureHoarderBtn = new Text2('Treasure Hoarder (200)', {
size: 50,
fill: 0xFFD700
});
treasureHoarderBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(treasureHoarderBtn);
treasureHoarderBtn.y = -20;
treasureHoarderBtn.x = 0;
// Button interactions
canadianBtn.down = function () {
selectedTowerType = 'canadian';
canadianBtn.fill = 0xFFD700;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
treasureHoarderBtn.fill = 0xFFD700;
};
pieShooterBtn.down = function () {
selectedTowerType = 'pieShooter';
pieShooterBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pyroCatalystBtn.fill = 0xFF4500;
};
iceBtn.down = function () {
selectedTowerType = 'ice';
iceBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
hammerBtn.down = function () {
selectedTowerType = 'hammer';
hammerBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
sunBtn.down = function () {
selectedTowerType = 'sun';
sunBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
moonBtn.down = function () {
selectedTowerType = 'moon';
moonBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
antiVirusBtn.down = function () {
selectedTowerType = 'antiVirus';
antiVirusBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
pyroCatalystBtn.down = function () {
selectedTowerType = 'pyroCatalyst';
pyroCatalystBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
treasureHoarderBtn.fill = 0xFFD700;
};
treasureHoarderBtn.down = function () {
selectedTowerType = 'treasureHoarder';
treasureHoarderBtn.fill = 0x00FF00;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
// Initialize first wave
spawnWave();
}
// Helper functions
function updateResourceDisplay() {
resourceTxt.setText('Resources: ' + resources);
}
function updateLivesDisplay() {
livesTxt.setText('Lives: ' + lives);
}
function updateWaveDisplay() {
var worldName = 'Normal';
if (currentWorld === 'sun') {
worldName = 'Sun World';
}
if (currentWorld === 'moon') {
worldName = 'Moon World';
}
waveTxt.setText('Episode ' + episode + ' - Wave: ' + wave + ' (' + worldName + ')');
}
function findNearestEnemy(x, y, range) {
var nearest = null;
var nearestDistance = range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed) {
var dx = enemy.x - x;
var dy = enemy.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearest = enemy;
nearestDistance = distance;
}
}
}
return nearest;
}
function findNearestTower(x, y, range) {
var nearest = null;
var nearestDistance = range;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (!tower.destroyed) {
var dx = tower.x - x;
var dy = tower.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearest = tower;
nearestDistance = distance;
}
}
}
return nearest;
}
function spawnWave() {
for (var i = 0; i < enemiesPerWave + Math.floor(wave / 3); i++) {
LK.setTimeout(function () {
var enemy;
var rand = Math.random();
// Episode-based enemy spawning
if (episode >= 3 && rand < 0.15) {
// Moon World - Red Moon enemies
enemy = new RedMoonEmdenGoose();
} else if (episode >= 2 && rand < 0.15) {
// Sun World - Blue Sun enemies
enemy = new BlueSunEmdenGoose();
} else if (wave >= 3 && rand < 0.2) {
// Hydro Arrow Emden
enemy = new HydroArrowEmdenGoose();
} else if (wave >= 2 && rand < 0.45) {
// Ameno Emden Potioneer
enemy = new AmenoEmdenGoose();
} else {
enemy = new EmdenGoose();
}
enemy.x = pathPoints[0].x;
enemy.y = pathPoints[0].y;
enemy.health = enemy.maxHealth + (wave - 1) * 20;
enemy.maxHealth = enemy.health;
enemies.push(enemy);
game.addChild(enemy);
}, i * 1000);
}
}
// Game interaction
game.down = function (x, y, obj) {
if (gameState !== 'playing') {
return;
}
// Check if clicking on path (don't allow tower placement)
var onPath = false;
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var dx = end.x - start.x;
var dy = end.y - start.y;
var length = Math.sqrt(dx * dx + dy * dy);
var nx = dx / length;
var ny = dy / length;
var px = x - start.x;
var py = y - start.y;
var dot = px * nx + py * ny;
if (dot >= 0 && dot <= length) {
var distance = Math.abs(px * ny - py * nx);
if (distance < 60) {
onPath = true;
break;
}
}
}
if (!onPath) {
var tower = null;
var cost = 0;
if (selectedTowerType === 'canadian') {
tower = new CanadianGoose();
cost = tower.cost;
} else if (selectedTowerType === 'ice') {
tower = new IceGoose();
cost = tower.cost;
} else if (selectedTowerType === 'hammer') {
tower = new HammerGoose();
cost = tower.cost;
} else if (selectedTowerType === 'sun') {
tower = new SunShooter();
cost = tower.cost;
} else if (selectedTowerType === 'moon') {
tower = new MoonlightSleeper();
cost = tower.cost;
} else if (selectedTowerType === 'antiVirus') {
tower = new AntiVirusGoose();
cost = tower.cost;
} else if (selectedTowerType === 'pieShooter') {
tower = new PieShooterGoose();
cost = tower.cost;
} else if (selectedTowerType === 'pyroCatalyst') {
tower = new PyroCatalystGoose();
cost = tower.cost;
} else if (selectedTowerType === 'treasureHoarder') {
tower = new TreasureHoarderGoose();
cost = tower.cost;
}
if (tower && resources >= cost) {
tower.x = x;
tower.y = y;
// Apply world modifiers
if (tower.worldModifier !== undefined) {
if (currentWorld === 'sun' && selectedTowerType === 'sun') {
tower.worldModifier = 1.5; // Strong in Sun World
} else if (currentWorld === 'moon' && selectedTowerType === 'moon') {
tower.worldModifier = 1.5; // Strong in Moon World
} else if (currentWorld === 'sun' && selectedTowerType === 'moon') {
tower.worldModifier = 0.7; // Weak in wrong world
} else if (currentWorld === 'moon' && selectedTowerType === 'sun') {
tower.worldModifier = 0.7; // Weak in wrong world
}
}
towers.push(tower);
game.addChild(tower);
resources -= cost;
updateResourceDisplay();
}
}
};
// Show main menu on startup
createMainMenu();
// Main game loop
game.update = function () {
if (gameState !== 'playing') {
return;
}
waveTimer++;
// Clean up destroyed enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].destroyed) {
enemies.splice(i, 1);
}
}
// Clean up destroyed projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
if (projectiles[i].destroyed) {
projectiles.splice(i, 1);
}
}
// Clean up destroyed potions
for (var i = potions.length - 1; i >= 0; i--) {
if (potions[i].destroyed) {
potions.splice(i, 1);
}
}
// Check if wave is complete and spawn next wave
if (enemies.length === 0 && waveTimer >= waveDelay) {
wave++;
// Episode progression logic
if (wave > 10 && episode === 1) {
episode = 2;
currentWorld = 'sun';
wave = 1;
game.setBackgroundColor(0xFFD700); // Golden sun background
// Update all Sun Shooter towers to be strong
for (var t = 0; t < towers.length; t++) {
if (towers[t].worldModifier !== undefined) {
if (towers[t].attachedAsset === 'sunShooter') {
towers[t].worldModifier = 1.5;
tween(towers[t], {
tint: 0xFFD700
}, {
duration: 1000
});
} else if (towers[t].attachedAsset === 'moonlightSleeper') {
towers[t].worldModifier = 0.7;
tween(towers[t], {
tint: 0x666666
}, {
duration: 1000
});
}
}
}
} else if (wave > 10 && episode === 2) {
episode = 3;
currentWorld = 'moon';
wave = 1;
game.setBackgroundColor(0x191970); // Dark blue moon background
// Update all Moonlight Sleeper towers to be strong
for (var t = 0; t < towers.length; t++) {
if (towers[t].worldModifier !== undefined) {
if (towers[t].attachedAsset === 'moonlightSleeper') {
towers[t].worldModifier = 1.5;
tween(towers[t], {
tint: 0x9370DB
}, {
duration: 1000
});
} else if (towers[t].attachedAsset === 'sunShooter') {
towers[t].worldModifier = 0.7;
tween(towers[t], {
tint: 0x666666
}, {
duration: 1000
});
}
}
}
} else if (wave > 10 && episode === 3) {
LK.showYouWin();
}
updateWaveDisplay();
waveTimer = 0;
spawnWave();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AmenoEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('amenoGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 150;
self.speed = 1.2;
self.pathIndex = 0;
self.reward = 20;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.potionTimer = 0;
self.potionRate = 240; // 4 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff513a, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC; // Purple tint for stun
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.throwPotion = function () {
var nearestTower = findNearestTower(self.x, self.y, 200);
if (nearestTower) {
var potion = new PoisonPotion();
potion.x = self.x;
potion.y = self.y;
potion.target = nearestTower;
potions.push(potion);
game.addChild(potion);
LK.getSound('potion').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0xFFFFFF;
}
}
// Handle stun timer
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0xFFFFFF;
}
}
// Potion throwing ability
self.potionTimer++;
if (self.potionTimer >= self.potionRate && !self.frozen && !self.stunned) {
self.throwPotion();
self.potionTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var AntiVirusGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('antiVirusGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x00FF00; // Green anti-virus color
self.range = 160;
self.cleanseRate = 120; // 2 seconds at 60fps
self.cleanseTimer = 0;
self.cost = 150;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.cleanseTimer++;
if (self.cleanseTimer >= self.cleanseRate) {
// Find all towers in range that are disabled
var towersInRange = [];
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (!tower.destroyed && tower.disabled) {
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
towersInRange.push(tower);
}
}
}
// Cleanse all disabled towers in range
for (var j = 0; j < towersInRange.length; j++) {
var tower = towersInRange[j];
tower.disabled = false;
tower.disableTimer = 0;
// Visual cleansing effect
LK.effects.flashObject(tower, 0x00FF00, 800);
tween(tower, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
if (towersInRange.length > 0) {
// Visual effect for anti-virus goose
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0x00FF00
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.getSound('antiVirus').play();
}
self.cleanseTimer = 0;
}
}
};
return self;
});
var BlueSunEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('blueSunGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x4169E1; // Royal blue color
self.health = 200;
self.maxHealth = 200;
self.speed = 1.3;
self.pathIndex = 0;
self.reward = 35;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.swordTimer = 0;
self.swordRate = 150; // 2.5 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff1e00, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC;
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.swordSlash = function () {
var nearestTower = findNearestTower(self.x, self.y, 180);
if (nearestTower) {
var slash = new BlueSunSlash();
slash.x = self.x;
slash.y = self.y;
slash.target = nearestTower;
potions.push(slash);
game.addChild(slash);
LK.getSound('blueSunSword').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0x4169E1;
}
}
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0x4169E1;
}
}
self.swordTimer++;
if (self.swordTimer >= self.swordRate && !self.frozen && !self.stunned) {
self.swordSlash();
self.swordTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var BlueSunSlash = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x4169E1; // Royal blue color
self.speed = 8;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Blue sun slash disables tower for 4 seconds
self.target.disabled = true;
self.target.disableTimer = 240; // 4 seconds at 60fps
LK.effects.flashObject(self.target, 0x4169E1, 700);
// Apply world modifier weakness
if (self.target.worldModifier) {
self.target.worldModifier = 0.5; // Weakened in wrong world
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.worldModifier = 1.0;
}
}, 2500);
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var CanadianGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('canadianGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 150;
self.damage = 25;
self.fireRate = 60; // 1 second at 60fps
self.fireTimer = 0;
self.cost = 50;
self.disabled = false;
self.disableTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = self.damage;
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('honk').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var EmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('emdenGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 1.5;
self.pathIndex = 0;
self.reward = 10;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC; // Purple tint for stun
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
// Charm state variables
self.charmed = false;
self.charmTimer = 0;
self.originalSpeed = self.speed;
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0xFFFFFF;
}
}
// Handle stun timer
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0xFFFFFF;
}
}
// Handle charm timer - charmed enemies move backwards
if (self.charmed && self.charmTimer > 0) {
self.charmTimer--;
if (self.charmTimer <= 0) {
self.charmed = false;
self.speed = self.originalSpeed;
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
}
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target;
// If charmed, move towards previous path point (backwards)
if (self.charmed && self.pathIndex > 0) {
target = pathPoints[self.pathIndex - 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex--;
if (self.pathIndex <= 0) {
// If charmed enemy reaches start, remove it and give bonus resources
resources += self.reward * 2;
updateResourceDisplay();
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * Math.abs(self.speed);
self.y += Math.sin(angle) * Math.abs(self.speed);
}
} else {
// Normal forward movement
target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
}
};
return self;
});
var FakeEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('emdenGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden tint to show it's fake
self.charmRange = 100;
self.lifetime = 600; // 10 seconds at 60fps
self.timer = 0;
self.charmedEnemies = [];
self.update = function () {
self.timer++;
// Pulsing effect to show it's fake
graphics.alpha = 0.7 + Math.sin(self.timer * 0.1) * 0.3;
graphics.rotation += 0.02;
// Find nearby real enemies to charm
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && self.charmedEnemies.indexOf(enemy) === -1) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.charmRange) {
// Charm the enemy - make it move backwards on path
self.charmedEnemies.push(enemy);
enemy.charmed = true;
enemy.charmTimer = 300; // 5 seconds charm duration
enemy.originalSpeed = enemy.speed;
enemy.speed = -enemy.speed * 0.5; // Move backwards slower
// Visual charm effect
tween(enemy, {
tint: 0xFFB6C1
}, {
duration: 500,
easing: tween.easeOut
});
LK.effects.flashObject(enemy, 0xFFD700, 800);
}
}
}
// Remove charm from enemies over time
for (var j = self.charmedEnemies.length - 1; j >= 0; j--) {
var charmedEnemy = self.charmedEnemies[j];
if (charmedEnemy.destroyed || !charmedEnemy.charmed) {
self.charmedEnemies.splice(j, 1);
} else if (charmedEnemy.charmTimer <= 0) {
charmedEnemy.charmed = false;
charmedEnemy.speed = charmedEnemy.originalSpeed;
charmedEnemy.charmTimer = 0;
tween(charmedEnemy, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
self.charmedEnemies.splice(j, 1);
} else {
charmedEnemy.charmTimer--;
}
}
// Destroy fake goose after lifetime
if (self.timer >= self.lifetime) {
// Remove charm from all enemies when fake goose dies
for (var k = 0; k < self.charmedEnemies.length; k++) {
var enemy = self.charmedEnemies[k];
if (!enemy.destroyed) {
enemy.charmed = false;
enemy.speed = enemy.originalSpeed;
enemy.charmTimer = 0;
tween(enemy, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
}
}
self.destroy();
}
};
return self;
});
var FireMagic = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFF4500; // Orange-red fire color
self.speed = 6;
self.target = null;
self.damage = 45;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
// Primary fire damage
self.target.takeDamage(self.damage);
LK.effects.flashObject(self.target, 0xFF4500, 400);
// Burning damage over time - 6 ticks of burn damage
var burnDamage = 12;
var burnTicks = 6;
for (var bt = 1; bt <= burnTicks; bt++) {
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.takeDamage(burnDamage);
LK.effects.flashObject(self.target, 0xFF6600, 150);
}
}, bt * 500);
}
// Fire spreads to nearby enemies with area damage
var nearbyEnemies = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && enemy !== self.target) {
var edx = enemy.x - self.target.x;
var edy = enemy.y - self.target.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 90) {
nearbyEnemies.push(enemy);
}
}
}
for (var j = 0; j < nearbyEnemies.length; j++) {
var splashDamage = Math.floor(self.damage * 0.6);
nearbyEnemies[j].takeDamage(splashDamage);
LK.effects.flashObject(nearbyEnemies[j], 0xFF4500, 300);
// Reduced burning effect on nearby enemies
var nearbyBurnDamage = 8;
var nearbyBurnTicks = 3;
for (var nbt = 1; nbt <= nearbyBurnTicks; nbt++) {
LK.setTimeout(function () {
if (nearbyEnemies[j] && !nearbyEnemies[j].destroyed) {
nearbyEnemies[j].takeDamage(nearbyBurnDamage);
LK.effects.flashObject(nearbyEnemies[j], 0xFF6600, 100);
}
}, nbt * 600);
}
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var HammerGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hammerGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9932CC; // Electro purple color
self.range = 100;
self.stunDuration = 180; // 3 seconds at 60fps
self.fireRate = 90; // 1.5 seconds at 60fps
self.fireTimer = 0;
self.cost = 120;
self.disabled = false;
self.disableTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target && !target.stunned) {
// Hammer swing animation
tween(graphics, {
rotation: graphics.rotation + Math.PI / 4
}, {
duration: 150,
easing: tween.easeOut
});
tween(graphics, {
rotation: graphics.rotation
}, {
duration: 150,
easing: tween.easeIn
});
// Stun the enemy
target.stun(self.stunDuration);
LK.getSound('hammer').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var HydroArrow = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x0066CC; // Blue hydro color
self.speed = 7;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Disable tower for 4 seconds and deal damage
self.target.disabled = true;
self.target.disableTimer = 240; // 4 seconds at 60fps
LK.effects.flashObject(self.target, 0x0066CC, 600);
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var HydroArrowEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hydroGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x0066CC; // Blue hydro color
self.health = 150;
self.maxHealth = 150;
self.speed = 1.0;
self.pathIndex = 0;
self.reward = 30;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.arrowTimer = 0;
self.arrowRate = 180; // 3 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff1e00, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC; // Purple tint for stun
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.shootArrow = function () {
var nearestTower = findNearestTower(self.x, self.y, 250);
if (nearestTower) {
var arrow = new HydroArrow();
arrow.x = self.x;
arrow.y = self.y;
arrow.target = nearestTower;
potions.push(arrow);
game.addChild(arrow);
LK.getSound('hydroArrow').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0x0066CC; // Return to blue hydro color
}
}
// Handle stun timer
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0x0066CC; // Return to blue hydro color
}
}
// Hydro arrow shooting ability
self.arrowTimer++;
if (self.arrowTimer >= self.arrowRate && !self.frozen && !self.stunned) {
self.shootArrow();
self.arrowTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var IceEffect = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('iceEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
self.duration = 180; // 3 seconds at 60fps
self.timer = 0;
self.update = function () {
self.timer++;
graphics.rotation += 0.1;
if (self.timer >= self.duration) {
self.destroy();
}
};
return self;
});
var IceGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('iceGoose', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 120;
self.freezeDuration = 120; // 2 seconds at 60fps
self.fireRate = 180; // 3 seconds at 60fps
self.fireTimer = 0;
self.cost = 80;
self.disabled = false;
self.disableTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target && !target.frozen) {
target.freeze(self.freezeDuration);
LK.getSound('freeze').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var MoonMagic = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9370DB; // Purple moonlight color
self.speed = 7;
self.target = null;
self.damage = 40;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
LK.effects.flashObject(self.target, 0x9370DB, 400);
// Moonlight magic spreads to nearby enemies
var nearbyEnemies = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && enemy !== self.target) {
var edx = enemy.x - self.target.x;
var edy = enemy.y - self.target.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 80) {
nearbyEnemies.push(enemy);
}
}
}
for (var j = 0; j < nearbyEnemies.length; j++) {
nearbyEnemies[j].takeDamage(Math.floor(self.damage * 0.5));
LK.effects.flashObject(nearbyEnemies[j], 0x9370DB, 200);
}
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroy();
}
};
return self;
});
var MoonlightSleeper = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('moonlightSleeper', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9370DB; // Purple moonlight color
self.range = 200;
self.damage = 40;
self.fireRate = 90; // Slower but powerful magic
self.fireTimer = 0;
self.cost = 120;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0; // Damage modifier based on world
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new MoonMagic();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('moonMagic').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var PieProjectile = Container.expand(function () {
var self = Container.call(this);
self.pieType = 'apple'; // 'apple', 'cherry', 'blueberry'
self.speed = 6;
self.target = null;
self.damage = 30;
self.destroyed = false;
// Use appropriate pie asset based on type
var assetId = 'applePie';
if (self.pieType === 'cherry') assetId = 'cherryPie';
if (self.pieType === 'blueberry') assetId = 'blueberryPie';
var graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
// Apply pie-specific effects on enemies
if (self.pieType === 'apple') {
// Apple pie: slow enemy for 3 seconds
self.target.takeDamage(self.damage);
if (!self.target.frozen && !self.target.stunned) {
var originalSpeed = self.target.speed;
self.target.speed = originalSpeed * 0.5;
tween(self.target, {
tint: 0x90EE90
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.target, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.speed = originalSpeed;
}
}, 3000);
}
} else if (self.pieType === 'cherry') {
// Cherry pie: burning damage over time
self.target.takeDamage(self.damage);
var burnDamage = 8;
var burnTicks = 5;
for (var bt = 1; bt <= burnTicks; bt++) {
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.takeDamage(burnDamage);
LK.effects.flashObject(self.target, 0xFF6B6B, 200);
}
}, bt * 600);
}
} else if (self.pieType === 'blueberry') {
// Blueberry pie: chance to freeze enemy
self.target.takeDamage(self.damage);
if (Math.random() < 0.4 && !self.target.frozen) {
self.target.freeze(120); // 2 seconds freeze
}
}
// Pie splash effect with flavor-specific colors
var splashColor = 0x90EE90;
if (self.pieType === 'cherry') splashColor = 0xFF6B6B;
if (self.pieType === 'blueberry') splashColor = 0x4169E1;
LK.effects.flashObject(self.target, splashColor, 400);
// Area damage to nearby enemies
var nearbyEnemies = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed && enemy !== self.target) {
var edx = enemy.x - self.target.x;
var edy = enemy.y - self.target.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 60) {
nearbyEnemies.push(enemy);
}
}
}
for (var j = 0; j < nearbyEnemies.length; j++) {
var splashDamage = Math.floor(self.damage * 0.4);
nearbyEnemies[j].takeDamage(splashDamage);
LK.effects.flashObject(nearbyEnemies[j], splashColor, 200);
// Apply reduced pie effects to nearby enemies
if (self.pieType === 'apple' && !nearbyEnemies[j].frozen && !nearbyEnemies[j].stunned) {
var enemySpeed = nearbyEnemies[j].speed;
nearbyEnemies[j].speed = enemySpeed * 0.7;
LK.setTimeout(function () {
if (nearbyEnemies[j] && !nearbyEnemies[j].destroyed) {
nearbyEnemies[j].speed = enemySpeed;
}
}, 1500);
} else if (self.pieType === 'blueberry' && Math.random() < 0.2 && !nearbyEnemies[j].frozen) {
nearbyEnemies[j].freeze(60);
}
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var PieShooterGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pieShooterGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFB6C1; // Light pink color for pie shooter
self.range = 160;
self.damage = 30;
self.fireRate = 75; // Moderate shooting speed
self.fireTimer = 0;
self.cost = 110;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
self.healRange = 120;
self.healRate = 180; // 3 seconds at 60fps
self.healTimer = 0;
self.healAmount = 15;
self.lastPieType = 'apple'; // Track last pie type for healing effects
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
self.healTimer++;
// Shooting pies at enemies
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new PieProjectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
// Randomly select pie type
var pieTypes = ['apple', 'cherry', 'blueberry'];
projectile.pieType = pieTypes[Math.floor(Math.random() * pieTypes.length)];
self.lastPieType = projectile.pieType; // Track for healing effects
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('pieShot').play();
self.fireTimer = 0;
}
}
// Healing nearby Canadian Geese towers
if (self.healTimer >= self.healRate) {
var towersInRange = [];
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (!tower.destroyed && (tower.constructor === CanadianGoose || tower.constructor === PieShooterGoose)) {
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.healRange && tower !== self) {
towersInRange.push(tower);
}
}
}
// Heal all Canadian Geese in range with different pie healing effects
for (var j = 0; j < towersInRange.length; j++) {
var tower = towersInRange[j];
// Determine healing type based on what pie flavor was last shot
var currentPieType = 'apple'; // default
if (self.lastPieType) {
currentPieType = self.lastPieType;
}
// Apply different healing effects based on pie flavor
if (currentPieType === 'apple') {
// Apple healing: boost fire rate temporarily
if (tower.fireRate && !tower.appleBoosted) {
tower.appleBoosted = true;
var originalFireRate = tower.fireRate;
tower.fireRate = Math.floor(originalFireRate * 0.7); // 30% faster
tween(tower, {
tint: 0x90EE90
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (tower && !tower.destroyed) {
tower.fireRate = originalFireRate;
tower.appleBoosted = false;
}
}, 5000);
}
} else if (currentPieType === 'cherry') {
// Cherry healing: boost damage temporarily
if (tower.damage && !tower.cherryBoosted) {
tower.cherryBoosted = true;
var originalDamage = tower.damage;
tower.damage = Math.floor(originalDamage * 1.3); // 30% more damage
tween(tower, {
tint: 0xFF6B6B
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (tower && !tower.destroyed) {
tower.damage = originalDamage;
tower.cherryBoosted = false;
}
}, 5000);
}
} else if (currentPieType === 'blueberry') {
// Blueberry healing: boost range temporarily
if (tower.range && !tower.blueberryBoosted) {
tower.blueberryBoosted = true;
var originalRange = tower.range;
tower.range = Math.floor(originalRange * 1.25); // 25% more range
tween(tower, {
tint: 0x4169E1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.setTimeout(function () {
if (tower && !tower.destroyed) {
tower.range = originalRange;
tower.blueberryBoosted = false;
}
}, 5000);
}
}
// Base visual healing effect
LK.effects.flashObject(tower, 0x00FF7F, 600);
tween(tower, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeIn
});
}
});
}
if (towersInRange.length > 0) {
// Visual effect for pie shooter goose healing
tween(graphics, {
tint: 0x00FF7F
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFB6C1
}, {
duration: 400,
easing: tween.easeIn
});
}
});
}
self.healTimer = 0;
}
}
};
return self;
});
var PoisonPotion = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x9932CC; // Purple color for poison
self.speed = 6;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Disable tower for 3 seconds
self.target.disabled = true;
self.target.disableTimer = 180; // 3 seconds at 60fps
LK.effects.flashObject(self.target, 0x9932CC, 500);
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.target = null;
self.damage = 25;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroy();
}
};
return self;
});
var PyroCatalystGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pyroCatalystGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFF4500; // Orange-red fire color
self.range = 170;
self.damage = 45;
self.fireRate = 80; // Moderate casting speed
self.fireTimer = 0;
self.cost = 140;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
// Fire magic charging effect
self.chargingEffect = false;
self.chargeTimer = 0;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
// Charging effect before casting
if (self.fireTimer >= self.fireRate - 20 && !self.chargingEffect) {
self.chargingEffect = true;
tween(graphics, {
tint: 0xFFFF00,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut
});
}
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new FireMagic();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('fireMagic').play();
// Fire casting animation
tween(graphics, {
rotation: graphics.rotation + Math.PI / 6
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
rotation: graphics.rotation,
tint: 0xFF4500,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
self.fireTimer = 0;
self.chargingEffect = false;
}
}
}
};
return self;
});
var RedMoonEmdenGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('redMoonGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x8B0000; // Dark red color
self.health = 180;
self.maxHealth = 180;
self.speed = 1.1;
self.pathIndex = 0;
self.reward = 40;
self.frozen = false;
self.freezeTimer = 0;
self.destroyed = false;
self.magicTimer = 0;
self.magicRate = 200; // 3.3 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff1e00, 200);
if (self.health <= 0) {
resources += self.reward;
updateResourceDisplay();
LK.getSound('defeat').play();
self.destroyed = true;
self.destroy();
}
};
self.freeze = function (duration) {
self.frozen = true;
self.freezeTimer = duration;
graphics.tint = 0xAADDFF;
var iceEffect = new IceEffect();
iceEffect.x = self.x;
iceEffect.y = self.y;
game.addChild(iceEffect);
};
self.stunned = false;
self.stunTimer = 0;
self.stun = function (duration) {
self.stunned = true;
self.stunTimer = duration;
graphics.tint = 0x9932CC;
var stunEffect = new StunEffect();
stunEffect.x = self.x;
stunEffect.y = self.y;
game.addChild(stunEffect);
};
self.castRedMoonMagic = function () {
var nearestTower = findNearestTower(self.x, self.y, 220);
if (nearestTower) {
var magic = new RedMoonMagic();
magic.x = self.x;
magic.y = self.y;
magic.target = nearestTower;
potions.push(magic);
game.addChild(magic);
LK.getSound('redMoonMagic').play();
}
};
self.update = function () {
if (self.freezeTimer > 0) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.frozen = false;
graphics.tint = 0x8B0000;
}
}
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.stunned = false;
graphics.tint = 0x8B0000;
}
}
self.magicTimer++;
if (self.magicTimer >= self.magicRate && !self.frozen && !self.stunned) {
self.castRedMoonMagic();
self.magicTimer = 0;
}
if (!self.frozen && !self.stunned && self.pathIndex < pathPoints.length - 1) {
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= pathPoints.length - 1) {
lives--;
updateLivesDisplay();
if (lives <= 0) {
LK.showGameOver();
}
self.destroyed = true;
self.destroy();
return;
}
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
};
return self;
});
var RedMoonMagic = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x8B0000; // Dark red color
self.speed = 6;
self.target = null;
self.destroyed = false;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 25) {
// Red moon magic disables tower for 5 seconds and deals damage
self.target.disabled = true;
self.target.disableTimer = 300; // 5 seconds at 60fps
LK.effects.flashObject(self.target, 0x8B0000, 800);
// Apply world modifier weakness
if (self.target.worldModifier) {
self.target.worldModifier = 0.5; // Weakened in wrong world
LK.setTimeout(function () {
if (self.target && !self.target.destroyed) {
self.target.worldModifier = 1.0;
}
}, 3000);
}
self.destroyed = true;
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroyed = true;
self.destroy();
}
};
return self;
});
var StunEffect = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('stunEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
graphics.tint = 0x9932CC; // Purple color for stun effect
self.duration = 120; // 2 seconds at 60fps
self.timer = 0;
self.update = function () {
self.timer++;
graphics.rotation += 0.15;
graphics.scaleX = 1 + Math.sin(self.timer * 0.2) * 0.1;
graphics.scaleY = 1 + Math.sin(self.timer * 0.2) * 0.1;
if (self.timer >= self.duration) {
self.destroy();
}
};
return self;
});
var SunArrow = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden sun color
self.speed = 9;
self.target = null;
self.damage = 35;
self.update = function () {
if (self.target && !self.target.destroyed) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
LK.effects.flashObject(self.target, 0xFFD700, 300);
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.destroy();
}
};
return self;
});
var SunShooter = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('sunShooter', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden sun color
self.range = 180;
self.damage = 35;
self.fireRate = 45; // Fast shooting
self.fireTimer = 0;
self.cost = 100;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0; // Damage modifier based on world
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
var target = findNearestEnemy(self.x, self.y, self.range);
if (target) {
var projectile = new SunArrow();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = Math.floor(self.damage * self.worldModifier);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('sunArrow').play();
self.fireTimer = 0;
}
}
}
};
return self;
});
var TreasureHoarderGoose = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('treasureHoarderGoose', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Golden treasure color
self.range = 200;
self.fakeGooseRate = 480; // 8 seconds at 60fps
self.fakeGooseTimer = 0;
self.cost = 200;
self.disabled = false;
self.disableTimer = 0;
self.worldModifier = 1.0;
// Treasure collection ability - gains resources over time
self.treasureRate = 300; // 5 seconds at 60fps
self.treasureTimer = 0;
self.treasureAmount = 15;
self.update = function () {
// Handle disable timer
if (self.disableTimer > 0) {
self.disableTimer--;
if (self.disableTimer <= 0) {
self.disabled = false;
}
}
if (!self.disabled) {
self.fakeGooseTimer++;
self.treasureTimer++;
// Treasure hoarding - generate resources
if (self.treasureTimer >= self.treasureRate) {
resources += self.treasureAmount;
updateResourceDisplay();
// Treasure collection visual effect
tween(graphics, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FF00
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFD700
}, {
duration: 200,
easing: tween.easeIn
});
}
});
self.treasureTimer = 0;
}
// Create fake Emden goose to charm enemies
if (self.fakeGooseTimer >= self.fakeGooseRate) {
var nearestEnemy = findNearestEnemy(self.x, self.y, self.range);
if (nearestEnemy) {
var fakeGoose = new FakeEmdenGoose();
// Position fake goose strategically near enemy path
var targetPathPoint = pathPoints[Math.min(nearestEnemy.pathIndex + 1, pathPoints.length - 1)];
fakeGoose.x = targetPathPoint.x + (Math.random() - 0.5) * 120;
fakeGoose.y = targetPathPoint.y + (Math.random() - 0.5) * 120;
game.addChild(fakeGoose);
// Summoning visual effect
tween(graphics, {
rotation: graphics.rotation + Math.PI / 3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
rotation: graphics.rotation
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(self, 0xFFD700, 600);
LK.getSound('charm').play();
}
self.fakeGooseTimer = 0;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Game state variables
var gameState = 'menu'; // 'menu', 'playing', 'paused'
var mainMenu;
var gameInitialized = false;
// Game variables
var resources = 999999;
var lives = 20;
var wave = 1;
var enemies = [];
var towers = [];
var projectiles = [];
var potions = [];
var waveTimer = 0;
var waveDelay = 300; // 5 seconds between waves
var enemiesPerWave = 5;
var selectedTowerType = 'canadian';
var currentWorld = 'normal'; // 'normal', 'sun', 'moon'
var episode = 1;
// UI Elements - declare globally to be accessible
var resourceTxt;
var livesTxt;
var waveTxt;
var canadianBtn;
var iceBtn;
var hammerBtn;
var sunBtn;
var moonBtn;
var antiVirusBtn;
var pieShooterBtn;
var pyroCatalystBtn;
var treasureHoarderBtn;
// Path points for enemies to follow
var pathPoints = [{
x: 0,
y: 400
}, {
x: 300,
y: 400
}, {
x: 300,
y: 800
}, {
x: 700,
y: 800
}, {
x: 700,
y: 1200
}, {
x: 1100,
y: 1200
}, {
x: 1100,
y: 1600
}, {
x: 1500,
y: 1600
}, {
x: 1500,
y: 2000
}, {
x: 2048,
y: 2000
}];
// Initialize Main Menu
function createMainMenu() {
mainMenu = new Container();
// Title
var titleText = new Text2('Canadian Geese Tower Defense', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
mainMenu.addChild(titleText);
// Subtitle
var subtitleText = new Text2('Defend Against the Emden Invasion!', {
size: 80,
fill: 0xFFD700
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 550;
mainMenu.addChild(subtitleText);
// Play Button
var playButton = new Text2('START GAME', {
size: 100,
fill: 0x00FF00
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 1024;
playButton.y = 800;
playButton.down = function () {
startGame();
};
mainMenu.addChild(playButton);
// Instructions
var instructText = new Text2('Tap to place towers and defend your treasure!', {
size: 60,
fill: 0xCCCCCC
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1000;
mainMenu.addChild(instructText);
// Episode info
var episodeText = new Text2('3 Episodes: Normal → Sun World → Moon World', {
size: 50,
fill: 0x87CEEB
});
episodeText.anchor.set(0.5, 0.5);
episodeText.x = 1024;
episodeText.y = 1150;
mainMenu.addChild(episodeText);
// Animation for title
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
yoyo: true,
repeat: -1
});
// Animation for play button
tween(playButton, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
yoyo: true,
repeat: -1
});
game.addChild(mainMenu);
}
function startGame() {
if (mainMenu) {
mainMenu.destroy();
mainMenu = null;
}
gameState = 'playing';
if (!gameInitialized) {
initializeGame();
gameInitialized = true;
}
}
function initializeGame() {
// Create path visuals
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var segments = Math.ceil(Math.sqrt((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / 100);
for (var j = 0; j < segments; j++) {
var t = j / segments;
var pathSegment = game.addChild(LK.getAsset('path', {
anchorX: 0.5,
anchorY: 0.5,
x: start.x + (end.x - start.x) * t,
y: start.y + (end.y - start.y) * t,
alpha: 0.3
}));
}
}
// Create treasure at end
var treasure = game.addChild(LK.getAsset('treasure', {
anchorX: 0.5,
anchorY: 0.5,
x: pathPoints[pathPoints.length - 1].x - 100,
y: pathPoints[pathPoints.length - 1].y
}));
// UI Elements
resourceTxt = new Text2('Resources: ' + resources, {
size: 60,
fill: 0xFFFFFF
});
resourceTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(resourceTxt);
resourceTxt.x = -300;
resourceTxt.y = 50;
livesTxt = new Text2('Lives: ' + lives, {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(livesTxt);
livesTxt.x = -300;
livesTxt.y = 120;
waveTxt = new Text2('Wave: ' + wave, {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(waveTxt);
waveTxt.x = 120;
waveTxt.y = 50;
// Tower selection buttons
canadianBtn = new Text2('Canadian Goose (50)', {
size: 50,
fill: 0x8B4513
});
canadianBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(canadianBtn);
canadianBtn.y = -200;
canadianBtn.x = -200;
iceBtn = new Text2('Ice Goose (80)', {
size: 50,
fill: 0x87CEEB
});
iceBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(iceBtn);
iceBtn.y = -200;
iceBtn.x = 0;
hammerBtn = new Text2('Hammer Goose (120)', {
size: 50,
fill: 0x9932CC
});
hammerBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(hammerBtn);
hammerBtn.y = -200;
hammerBtn.x = 200;
sunBtn = new Text2('Sun Shooter (100)', {
size: 50,
fill: 0xFFD700
});
sunBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(sunBtn);
sunBtn.y = -140;
sunBtn.x = -200;
moonBtn = new Text2('Moonlight Sleeper (120)', {
size: 50,
fill: 0x9370DB
});
moonBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(moonBtn);
moonBtn.y = -140;
moonBtn.x = 200;
antiVirusBtn = new Text2('Anti Virus (150)', {
size: 50,
fill: 0x00FF00
});
antiVirusBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(antiVirusBtn);
antiVirusBtn.y = -80;
antiVirusBtn.x = -100;
pieShooterBtn = new Text2('Pie Shooter (110)', {
size: 50,
fill: 0xFFB6C1
});
pieShooterBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(pieShooterBtn);
pieShooterBtn.y = -80;
pieShooterBtn.x = 50;
pyroCatalystBtn = new Text2('Pyro Catalyst (140)', {
size: 50,
fill: 0xFF4500
});
pyroCatalystBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(pyroCatalystBtn);
pyroCatalystBtn.y = -80;
pyroCatalystBtn.x = 150;
treasureHoarderBtn = new Text2('Treasure Hoarder (200)', {
size: 50,
fill: 0xFFD700
});
treasureHoarderBtn.anchor.set(0.5, 0);
LK.gui.bottom.addChild(treasureHoarderBtn);
treasureHoarderBtn.y = -20;
treasureHoarderBtn.x = 0;
// Button interactions
canadianBtn.down = function () {
selectedTowerType = 'canadian';
canadianBtn.fill = 0xFFD700;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
treasureHoarderBtn.fill = 0xFFD700;
};
pieShooterBtn.down = function () {
selectedTowerType = 'pieShooter';
pieShooterBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pyroCatalystBtn.fill = 0xFF4500;
};
iceBtn.down = function () {
selectedTowerType = 'ice';
iceBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
hammerBtn.down = function () {
selectedTowerType = 'hammer';
hammerBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
sunBtn.down = function () {
selectedTowerType = 'sun';
sunBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
moonBtn.down = function () {
selectedTowerType = 'moon';
moonBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
antiVirusBtn.down = function () {
selectedTowerType = 'antiVirus';
antiVirusBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
pyroCatalystBtn.down = function () {
selectedTowerType = 'pyroCatalyst';
pyroCatalystBtn.fill = 0xFFD700;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
treasureHoarderBtn.fill = 0xFFD700;
};
treasureHoarderBtn.down = function () {
selectedTowerType = 'treasureHoarder';
treasureHoarderBtn.fill = 0x00FF00;
canadianBtn.fill = 0x8B4513;
iceBtn.fill = 0x87CEEB;
hammerBtn.fill = 0x9932CC;
sunBtn.fill = 0xFFD700;
moonBtn.fill = 0x9370DB;
antiVirusBtn.fill = 0x00FF00;
pieShooterBtn.fill = 0xFFB6C1;
pyroCatalystBtn.fill = 0xFF4500;
};
// Initialize first wave
spawnWave();
}
// Helper functions
function updateResourceDisplay() {
resourceTxt.setText('Resources: ' + resources);
}
function updateLivesDisplay() {
livesTxt.setText('Lives: ' + lives);
}
function updateWaveDisplay() {
var worldName = 'Normal';
if (currentWorld === 'sun') {
worldName = 'Sun World';
}
if (currentWorld === 'moon') {
worldName = 'Moon World';
}
waveTxt.setText('Episode ' + episode + ' - Wave: ' + wave + ' (' + worldName + ')');
}
function findNearestEnemy(x, y, range) {
var nearest = null;
var nearestDistance = range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.destroyed) {
var dx = enemy.x - x;
var dy = enemy.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearest = enemy;
nearestDistance = distance;
}
}
}
return nearest;
}
function findNearestTower(x, y, range) {
var nearest = null;
var nearestDistance = range;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (!tower.destroyed) {
var dx = tower.x - x;
var dy = tower.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearest = tower;
nearestDistance = distance;
}
}
}
return nearest;
}
function spawnWave() {
for (var i = 0; i < enemiesPerWave + Math.floor(wave / 3); i++) {
LK.setTimeout(function () {
var enemy;
var rand = Math.random();
// Episode-based enemy spawning
if (episode >= 3 && rand < 0.15) {
// Moon World - Red Moon enemies
enemy = new RedMoonEmdenGoose();
} else if (episode >= 2 && rand < 0.15) {
// Sun World - Blue Sun enemies
enemy = new BlueSunEmdenGoose();
} else if (wave >= 3 && rand < 0.2) {
// Hydro Arrow Emden
enemy = new HydroArrowEmdenGoose();
} else if (wave >= 2 && rand < 0.45) {
// Ameno Emden Potioneer
enemy = new AmenoEmdenGoose();
} else {
enemy = new EmdenGoose();
}
enemy.x = pathPoints[0].x;
enemy.y = pathPoints[0].y;
enemy.health = enemy.maxHealth + (wave - 1) * 20;
enemy.maxHealth = enemy.health;
enemies.push(enemy);
game.addChild(enemy);
}, i * 1000);
}
}
// Game interaction
game.down = function (x, y, obj) {
if (gameState !== 'playing') {
return;
}
// Check if clicking on path (don't allow tower placement)
var onPath = false;
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var dx = end.x - start.x;
var dy = end.y - start.y;
var length = Math.sqrt(dx * dx + dy * dy);
var nx = dx / length;
var ny = dy / length;
var px = x - start.x;
var py = y - start.y;
var dot = px * nx + py * ny;
if (dot >= 0 && dot <= length) {
var distance = Math.abs(px * ny - py * nx);
if (distance < 60) {
onPath = true;
break;
}
}
}
if (!onPath) {
var tower = null;
var cost = 0;
if (selectedTowerType === 'canadian') {
tower = new CanadianGoose();
cost = tower.cost;
} else if (selectedTowerType === 'ice') {
tower = new IceGoose();
cost = tower.cost;
} else if (selectedTowerType === 'hammer') {
tower = new HammerGoose();
cost = tower.cost;
} else if (selectedTowerType === 'sun') {
tower = new SunShooter();
cost = tower.cost;
} else if (selectedTowerType === 'moon') {
tower = new MoonlightSleeper();
cost = tower.cost;
} else if (selectedTowerType === 'antiVirus') {
tower = new AntiVirusGoose();
cost = tower.cost;
} else if (selectedTowerType === 'pieShooter') {
tower = new PieShooterGoose();
cost = tower.cost;
} else if (selectedTowerType === 'pyroCatalyst') {
tower = new PyroCatalystGoose();
cost = tower.cost;
} else if (selectedTowerType === 'treasureHoarder') {
tower = new TreasureHoarderGoose();
cost = tower.cost;
}
if (tower && resources >= cost) {
tower.x = x;
tower.y = y;
// Apply world modifiers
if (tower.worldModifier !== undefined) {
if (currentWorld === 'sun' && selectedTowerType === 'sun') {
tower.worldModifier = 1.5; // Strong in Sun World
} else if (currentWorld === 'moon' && selectedTowerType === 'moon') {
tower.worldModifier = 1.5; // Strong in Moon World
} else if (currentWorld === 'sun' && selectedTowerType === 'moon') {
tower.worldModifier = 0.7; // Weak in wrong world
} else if (currentWorld === 'moon' && selectedTowerType === 'sun') {
tower.worldModifier = 0.7; // Weak in wrong world
}
}
towers.push(tower);
game.addChild(tower);
resources -= cost;
updateResourceDisplay();
}
}
};
// Show main menu on startup
createMainMenu();
// Main game loop
game.update = function () {
if (gameState !== 'playing') {
return;
}
waveTimer++;
// Clean up destroyed enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].destroyed) {
enemies.splice(i, 1);
}
}
// Clean up destroyed projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
if (projectiles[i].destroyed) {
projectiles.splice(i, 1);
}
}
// Clean up destroyed potions
for (var i = potions.length - 1; i >= 0; i--) {
if (potions[i].destroyed) {
potions.splice(i, 1);
}
}
// Check if wave is complete and spawn next wave
if (enemies.length === 0 && waveTimer >= waveDelay) {
wave++;
// Episode progression logic
if (wave > 10 && episode === 1) {
episode = 2;
currentWorld = 'sun';
wave = 1;
game.setBackgroundColor(0xFFD700); // Golden sun background
// Update all Sun Shooter towers to be strong
for (var t = 0; t < towers.length; t++) {
if (towers[t].worldModifier !== undefined) {
if (towers[t].attachedAsset === 'sunShooter') {
towers[t].worldModifier = 1.5;
tween(towers[t], {
tint: 0xFFD700
}, {
duration: 1000
});
} else if (towers[t].attachedAsset === 'moonlightSleeper') {
towers[t].worldModifier = 0.7;
tween(towers[t], {
tint: 0x666666
}, {
duration: 1000
});
}
}
}
} else if (wave > 10 && episode === 2) {
episode = 3;
currentWorld = 'moon';
wave = 1;
game.setBackgroundColor(0x191970); // Dark blue moon background
// Update all Moonlight Sleeper towers to be strong
for (var t = 0; t < towers.length; t++) {
if (towers[t].worldModifier !== undefined) {
if (towers[t].attachedAsset === 'moonlightSleeper') {
towers[t].worldModifier = 1.5;
tween(towers[t], {
tint: 0x9370DB
}, {
duration: 1000
});
} else if (towers[t].attachedAsset === 'sunShooter') {
towers[t].worldModifier = 0.7;
tween(towers[t], {
tint: 0x666666
}, {
duration: 1000
});
}
}
}
} else if (wave > 10 && episode === 3) {
LK.showYouWin();
}
updateWaveDisplay();
waveTimer = 0;
spawnWave();
}
};
Canadian Goose in robe with sword. In-Game asset. 2d. High contrast. No shadows
Emden Goose in Outfit of Treasure Hoarders. In-Game asset. 2d. High contrast. No shadows
A Canadian Goose in Magic Ice Mage Outfit armed with a magic ice staff. In-Game asset. 2d. High contrast. No shadows
Emden Goose in Cyan Outfit of Treasure Hoarders armed with cyan anemo potion. In-Game asset. 2d. High contrast. No shadows
Ice magic effect. In-Game asset. 2d. High contrast. No shadows
A emden goose in blue treasure hoarders outfit with blue hydro battle bow. In-Game asset. 2d. High contrast. No shadows
Treasure with canadian geese as guard No text on image!!. In-Game asset. 2d. High contrast. No shadows
A Canadian goose in armor armed electro purple battle hammer.. In-Game asset. 2d. No shadows
A Canadian Goose in Moon Mage Outfit with Moonlight Magic Staff. In-Game asset. 2d. High contrast. No shadows
Canadian Goose in Sun Warrior Outfit with Magic Sun Crossbow. In-Game asset. 2d. High contrast. No shadows
An Emden Goose in Blue Sun Treasure Hoarder Outfit with Blue Sun Long Sword. In-Game asset. 2d. High contrast. No shadows
A Emden Goose in Treasure Hoarder Red Moon Mage Outfit with Red Moon Magic Staff. In-Game asset. 2d. High contrast. No shadows
Purple Stun Aura, No text on image!. In-Game asset. 2d. High contrast. No shadows
A Canadian Goose in Green Glowing Anti Virus Outfit Armed with anti virus magic. In-Game asset. 2d
A Canadian Goose in maid chef outfit armed with spoon with tangerine pie on it. In-Game asset. 2d. High contrast. No shadows
Cherry pie with pink fire aura. In-Game asset. 2d. High contrast. No shadows
apple pie with green aura. In-Game asset. 2d. High contrast. No shadows
blueberry pie with frost aura. In-Game asset. 2d. High contrast. No shadows
A Canadian Goose in Pyro Mage outfit holding a pyro catalyst. In-Game asset. 2d. High contrast. No shadows
Canadian Goose in treasure hoarder outfit with claymore. In-Game asset. 2d. High contrast. No shadows