/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BossPigeon = Container.expand(function () {
var self = Container.call(this);
var pigeonGraphics = self.attachAsset('cyberPigeon', {
anchorX: 0.5,
anchorY: 0.5
});
pigeonGraphics.scaleX = 2;
pigeonGraphics.scaleY = 2;
self.health = Math.min(140 + 20 * wave, 520);
self.maxHealth = self.health;
self.speed = 1.2;
self.attackCooldown = 0;
self.specialAttackCooldown = 0;
self.startX = 1024;
self.isBoss = true;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
var damageText = new Text2('-' + damage, {
size: 60,
fill: 0xffff00
});
damageText.x = self.x;
damageText.y = self.y - 50;
game.addChild(damageText);
tween(damageText, {
y: damageText.y - 80,
alpha: 0
}, {
duration: 1000,
onComplete: function onComplete() {
damageText.destroy();
}
});
if (self.health <= 0) {
score += 200;
for (var i = 0; i < 4; i++) {
LK.setTimeout(function () {
var explosion = new Explosion();
explosion.x = self.x + (Math.random() - 0.5) * 120;
explosion.y = self.y + (Math.random() - 0.5) * 120;
game.addChild(explosion);
explosions.push(explosion);
LK.getSound('explosion').play();
}, i * 150);
}
for (var i = 0; i < 4; i++) {
spawnPowerUpAt(self.x + (Math.random() - 0.5) * 100, self.y + (Math.random() - 0.5) * 150);
}
return true;
}
return false;
};
self.update = function () {
var dy = 1366 - self.y;
var distance = Math.abs(dy);
if (distance > 300) {
self.y += self.speed;
self.x = self.startX + Math.sin(LK.ticks * 0.05) * 400;
} else {
self.x = self.startX + Math.sin(LK.ticks * 0.05) * 400;
}
if (self.specialAttackCooldown <= 0) {
for (var i = 0; i < 4; i++) {
var miniPigeon = new CyberPigeon();
miniPigeon.x = self.x + (i - 1.5) * 50; // Fila horizontal, espaciado de 100 píxeles
miniPigeon.y = self.y;
miniPigeon.health = 20;
cyberPigeons.push(miniPigeon);
game.addChild(miniPigeon);
}
self.specialAttackCooldown = 180;
} else {
self.specialAttackCooldown--;
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
pigeonGraphics.rotation = Math.atan2(dy, 1024 - self.x);
};
return self;
});
var CyberPigeon = Container.expand(function () {
var self = Container.call(this);
var type = Math.random() < 0.15 ? 'rotating' : Math.random() < 0.8235 ? 'normal' : 'fast';
var assetName = type === 'fast' ? 'fastPigeon' : type === 'rotating' ? 'rotatingPigeon' : 'cyberPigeon';
var pigeonGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.health = Math.min(35 + 4 * Math.sqrt(wave), 75);
self.maxHealth = self.health;
self.speed = Math.min(1.8 + 0.3 * Math.sqrt(wave), 8);
self.attackCooldown = 0;
self.type = type;
if (self.type === 'fast') {
self.speed *= 1.8;
self.health *= 0.6;
} else if (self.type === 'rotating') {
self.speed *= 1.2;
self.health *= 0.8;
self.orbitRadius = 50;
self.orbitAngle = Math.random() * Math.PI * 2;
self.orbitSpeed = 0.1;
}
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 150);
var damageText = new Text2('-' + damage, {
size: 40,
fill: 0xff0000
});
damageText.x = self.x;
damageText.y = self.y - 30;
game.addChild(damageText);
tween(damageText, {
y: damageText.y - 60,
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
damageText.destroy();
}
});
if (self.health <= 0) {
score += self.type === 'fast' ? 20 : self.type === 'rotating' ? 25 : 15;
var explosion = new Explosion();
explosion.x = self.x;
explosion.y = self.y;
game.addChild(explosion);
explosions.push(explosion);
LK.getSound('explosion').play();
return true;
}
return false;
};
self.update = function () {
var dx = 1024 - self.x;
var dy = 1366 - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
if (self.type === 'rotating') {
self.orbitAngle += self.orbitSpeed;
var orbitX = dx / distance * self.speed;
var orbitY = dy / distance * self.speed;
self.x += orbitX + Math.cos(self.orbitAngle) * self.orbitRadius * 0.1;
self.y += orbitY + Math.sin(self.orbitAngle) * self.orbitRadius * 0.1;
} else {
var wobble = self.type === 'fast' ? Math.sin(LK.ticks * 0.25) * 25 : 0;
self.x += dx / distance * self.speed + Math.sin(LK.ticks * 0.12) * 0.6;
self.y += dy / distance * self.speed + wobble * 0.12;
}
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
pigeonGraphics.rotation = Math.atan2(dy, dx);
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 35;
self.radius = 100;
explosionGraphics.scaleX = 0.2;
explosionGraphics.scaleY = 0.2;
self.update = function () {
self.lifeTime--;
var progress = 1 - self.lifeTime / 35;
explosionGraphics.scaleX = 0.2 + progress * 2.8;
explosionGraphics.scaleY = 0.2 + progress * 2.8;
explosionGraphics.alpha = 1 - progress;
explosionGraphics.rotation += 0.15;
return self.lifeTime <= 0;
};
return self;
});
var Grandma = Container.expand(function () {
var self = Container.call(this);
var grandmaGraphics = self.attachAsset('grandma', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 120;
self.maxHealth = 120;
self.bombDamage = 60;
self.shotCooldown = 0;
self.speedBoost = 0;
self.damageBoost = 0;
self.multishot = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
game.x = (Math.random() - 0.5) * 25;
game.y = (Math.random() - 0.5) * 25;
LK.setTimeout(function () {
game.x = 0;
game.y = 0;
}, 120);
if (self.health <= 0) {
LK.showGameOver();
}
};
self.update = function () {
self.x = 1024;
self.y = 1366;
if (self.health < self.maxHealth && LK.ticks % (wave > 10 ? 400 : 500) === 0) {
self.health = Math.min(self.health + (wave > 10 ? 3 : 2), self.maxHealth);
}
if (self.shotCooldown > 0) {
self.shotCooldown--;
}
};
return self;
});
var KetchupBomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('ketchupBomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifeTime = 80;
self.damage = grandma.bombDamage + grandma.damageBoost;
self.trail = [];
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.lifeTime--;
bombGraphics.rotation += 0.25;
self.trail.push({
x: self.x,
y: self.y,
alpha: 1
});
if (self.trail.length > 6) {
self.trail.shift();
}
if (self.lifeTime <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var rand = Math.random();
var types = rand < 0.1 ? 'multishot' : rand < 0.4 ? 'speed' : rand < 0.7 ? 'health' : 'damage';
self.type = types;
var assetName = 'powerUp' + self.type.charAt(0).toUpperCase() + self.type.slice(1);
var powerUpGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 800;
self.update = function () {
self.lifeTime--;
self.y += Math.sin(LK.ticks * 0.18) * 1.2;
powerUpGraphics.rotation += 0.001;
powerUpGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.12) * 0.25;
powerUpGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.12) * 0.25;
if (self.lifeTime < 160) {
powerUpGraphics.alpha = Math.sin(LK.ticks * 0.35) * 0.5 + 0.5;
}
return self.lifeTime <= 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var background = game.addChild(LK.getAsset('background', {
x: 0,
y: 0
}));
/****
* Game Variables
****/
var grandma;
var cyberPigeons = [];
var ketchupBombs = [];
var explosions = [];
var powerUps = [];
var score = 0;
var wave = 1;
var enemiesKilled = 0;
var enemiesThisWave = 0;
var totalEnemiesThisWave = 0;
var lastClickTime = 0;
var comboCounter = 0;
var comboTimer = 0;
var bossWave = false;
/****
* UI Elements
****/
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
scoreText.x = 50;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
var healthText = new Text2('Health: 120/120', {
size: 50,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 3
});
healthText.anchor.set(1, 0);
LK.gui.topRight.addChild(healthText);
var waveText = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 3
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var bossHealthBg = LK.getAsset('chargeBarBg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
x: 0,
y: 100
});
LK.gui.top.addChild(bossHealthBg);
bossHealthBg.visible = false;
var bossHealthBar = LK.getAsset('chargeBar', {
anchorX: 0,
anchorY: 0,
scaleX: 0,
x: -150,
y: 100
});
LK.gui.top.addChild(bossHealthBar);
bossHealthBar.visible = false;
var comboText = new Text2('', {
size: 60,
fill: 0x00FFFF,
stroke: 0x000000,
strokeThickness: 3
});
comboText.anchor.set(0.5, 0);
comboText.x = 1024;
comboText.y = 200;
game.addChild(comboText);
var waveProgressBg = LK.getAsset('chargeBarBg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
x: 0,
y: 60
});
LK.gui.top.addChild(waveProgressBg);
var waveProgressBar = LK.getAsset('chargeBar', {
anchorX: 0,
anchorY: 0,
scaleX: 0,
x: -150,
y: 60
});
LK.gui.top.addChild(waveProgressBar);
/****
* Game Functions
****/
function createGrandma() {
grandma = game.addChild(new Grandma());
grandma.x = 1024;
grandma.y = 1366;
}
function throwKetchupBomb(targetX, targetY) {
if (grandma.shotCooldown > 0) {
return;
}
LK.getSound('attack').play();
LK.effects.flashObject(grandma, 0xffff00, 150);
var shots = grandma.multishot > 0 ? 3 : 1;
var angles = [0];
if (shots > 1) {
angles = [-15, 0, 15];
}
var enemiesHit = 0;
for (var i = 0; i < shots; i++) {
var bomb = new KetchupBomb();
bomb.x = grandma.x;
bomb.y = grandma.y;
var angle = Math.atan2(targetY - grandma.y, targetX - grandma.x) + angles[i] * Math.PI / 180;
var dx = Math.cos(angle);
var dy = Math.sin(angle);
var speed = 12;
bomb.velocityX = dx * speed;
bomb.velocityY = dy * speed;
bomb.damage = grandma.bombDamage + grandma.damageBoost;
ketchupBombs.push(bomb);
game.addChild(bomb);
enemiesHit += checkImmediateHits(bomb, angle);
}
if (enemiesHit > 0) {
comboCounter += enemiesHit;
comboTimer = 150;
if (comboCounter >= 3) {
score += comboCounter * 6;
}
}
grandma.shotCooldown = 24 - Math.min(grandma.speedBoost * 2, 16);
}
function checkImmediateHits(bomb, angle) {
var enemiesHit = 0;
var range = 300;
for (var j = cyberPigeons.length - 1; j >= 0; j--) {
var pigeon = cyberPigeons[j];
var pdx = pigeon.x - grandma.x;
var pdy = pigeon.y - grandma.y;
var distance = Math.sqrt(pdx * pdx + pdy * pdy);
if (distance < range && Math.abs(Math.atan2(pdy, pdx) - angle) < 0.3) {
if (pigeon.takeDamage(bomb.damage)) {
pigeon.destroy();
cyberPigeons.splice(j, 1);
enemiesKilled++;
enemiesThisWave++;
enemiesHit++;
}
}
}
for (var j = powerUps.length - 1; j >= 0; j--) {
var powerUp = powerUps[j];
var pdx = powerUp.x - grandma.x;
var pdy = powerUp.y - grandma.y;
var distance = Math.sqrt(pdx * pdx + pdy * pdy);
if (distance < range && Math.abs(Math.atan2(pdy, pdx) - angle) < 0.3) {
collectPowerUp(powerUp);
powerUp.destroy();
powerUps.splice(j, 1);
}
}
return enemiesHit;
}
function spawnEnemies() {
bossWave = wave % 5 === 0;
if (bossWave) {
var boss = new BossPigeon();
boss.x = 1024;
boss.y = -150;
cyberPigeons.push(boss);
game.addChild(boss);
totalEnemiesThisWave = 1;
} else {
var maxEnemies = Math.min(15 + Math.floor(wave / 5), 30);
var enemiesToSpawn = Math.min(4 + wave, maxEnemies);
totalEnemiesThisWave = enemiesToSpawn;
for (var i = 0; i < enemiesToSpawn; i++) {
LK.setTimeout(function () {
var pigeon = new CyberPigeon();
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
pigeon.x = Math.random() * 2048;
pigeon.y = -50;
break;
case 1:
pigeon.x = 2098;
pigeon.y = Math.random() * 2732;
break;
case 2:
pigeon.x = Math.random() * 2048;
pigeon.y = 2782;
break;
case 3:
pigeon.x = -50;
pigeon.y = Math.random() * 2732;
break;
}
cyberPigeons.push(pigeon);
game.addChild(pigeon);
}, i * (wave > 10 ? 300 : 400));
}
}
enemiesThisWave = 0;
}
function spawnPowerUpAt(x, y) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
function collectPowerUp(powerUp) {
LK.getSound('powerup').play();
LK.effects.flashObject(grandma, 0x00ff00, 400);
var bonusText = new Text2('', {
size: 50,
fill: 0x00ff00
});
bonusText.x = grandma.x;
bonusText.y = grandma.y - 60;
game.addChild(bonusText);
switch (powerUp.type) {
case 'speed':
grandma.speedBoost += 2;
bonusText.setText('FIRE RATE UP!');
break;
case 'health':
grandma.health = Math.min(grandma.health + 30 + 2 * wave, grandma.maxHealth);
bonusText.setText('HEALTH +' + (30 + 2 * wave) + '!');
break;
case 'damage':
grandma.damageBoost += 12;
bonusText.setText('DAMAGE UP!');
break;
case 'multishot':
grandma.multishot = 180;
bonusText.setText('MULTISHOT!');
break;
}
tween(bonusText, {
y: bonusText.y - 100,
alpha: 0
}, {
duration: 1200,
onComplete: function onComplete() {
bonusText.destroy();
}
});
}
/****
* Event Handlers
****/
game.down = function (x, y, obj) {
throwKetchupBomb(x, y);
};
/****
* Game Loop
****/
createGrandma();
LK.playMusic('gameMusic');
spawnEnemies();
game.update = function () {
if (comboTimer > 0) {
comboTimer--;
if (comboCounter >= 3) {
comboText.setText('COMBO x' + comboCounter + '!');
} else {
comboText.setText('');
}
} else {
comboCounter = 0;
comboText.setText('');
}
for (var i = cyberPigeons.length - 1; i >= 0; i--) {
var pigeon = cyberPigeons[i];
pigeon.update();
if (pigeon.intersects(grandma) && pigeon.attackCooldown <= 0) {
var damage = pigeon.isBoss ? 20 : 10;
grandma.takeDamage(damage);
pigeon.attackCooldown = 50;
}
if (pigeon.x < -200 || pigeon.x > 2248 || pigeon.y < -200 || pigeon.y > 2932) {
pigeon.destroy();
cyberPigeons.splice(i, 1);
}
}
for (var i = ketchupBombs.length - 1; i >= 0; i--) {
var bomb = ketchupBombs[i];
var shouldRemove = bomb.update();
if (shouldRemove || bomb.x < -100 || bomb.x > 2148 || bomb.y < -100 || bomb.y > 2832) {
bomb.destroy();
ketchupBombs.splice(i, 1);
} else {
for (var j = cyberPigeons.length - 1; j >= 0; j--) {
var pigeon = cyberPigeons[j];
if (bomb.intersects(pigeon)) {
if (pigeon.takeDamage(bomb.damage)) {
pigeon.destroy();
cyberPigeons.splice(j, 1);
enemiesKilled++;
enemiesThisWave++;
}
bomb.destroy();
ketchupBombs.splice(i, 1);
break;
}
}
for (var j = powerUps.length - 1; j >= 0; j--) {
var powerUp = powerUps[j];
if (bomb.intersects(powerUp)) {
collectPowerUp(powerUp);
powerUp.destroy();
powerUps.splice(j, 1);
bomb.destroy();
ketchupBombs.splice(i, 1);
break;
}
}
}
}
for (var i = explosions.length - 1; i >= 0; i--) {
var explosion = explosions[i];
var shouldRemove = explosion.update();
if (shouldRemove) {
explosion.destroy();
explosions.splice(i, 1);
}
}
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
var shouldRemove = powerUp.update();
if (shouldRemove) {
powerUp.destroy();
powerUps.splice(i, 1);
}
}
if (grandma.multishot > 0) {
grandma.multishot--;
}
grandma.update();
if (cyberPigeons.length === 0) {
wave++;
spawnEnemies();
if (Math.random() < 1) {
spawnPowerUpAt(Math.random() * 1600 + 200, Math.random() * 2200 + 200);
}
}
scoreText.setText('Score: ' + score);
healthText.setText('Health: ' + grandma.health + '/' + grandma.maxHealth);
waveText.setText('Wave: ' + wave + (bossWave ? ' (BOSS)' : ''));
// Actualizar barra de vida del jefe
if (bossWave && cyberPigeons.length > 0) {
var boss = cyberPigeons.find(function (p) {
return p.isBoss;
});
if (boss) {
bossHealthBg.visible = true;
bossHealthBar.visible = true;
bossHealthBar.scaleX = boss.health / boss.maxHealth * 1.5;
}
} else {
bossHealthBg.visible = false;
bossHealthBar.visible = false;
}
var progress = totalEnemiesThisWave > 0 ? enemiesThisWave / totalEnemiesThisWave : 0;
waveProgressBar.scaleX = progress * 1.5;
LK.setScore(score);
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BossPigeon = Container.expand(function () {
var self = Container.call(this);
var pigeonGraphics = self.attachAsset('cyberPigeon', {
anchorX: 0.5,
anchorY: 0.5
});
pigeonGraphics.scaleX = 2;
pigeonGraphics.scaleY = 2;
self.health = Math.min(140 + 20 * wave, 520);
self.maxHealth = self.health;
self.speed = 1.2;
self.attackCooldown = 0;
self.specialAttackCooldown = 0;
self.startX = 1024;
self.isBoss = true;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
var damageText = new Text2('-' + damage, {
size: 60,
fill: 0xffff00
});
damageText.x = self.x;
damageText.y = self.y - 50;
game.addChild(damageText);
tween(damageText, {
y: damageText.y - 80,
alpha: 0
}, {
duration: 1000,
onComplete: function onComplete() {
damageText.destroy();
}
});
if (self.health <= 0) {
score += 200;
for (var i = 0; i < 4; i++) {
LK.setTimeout(function () {
var explosion = new Explosion();
explosion.x = self.x + (Math.random() - 0.5) * 120;
explosion.y = self.y + (Math.random() - 0.5) * 120;
game.addChild(explosion);
explosions.push(explosion);
LK.getSound('explosion').play();
}, i * 150);
}
for (var i = 0; i < 4; i++) {
spawnPowerUpAt(self.x + (Math.random() - 0.5) * 100, self.y + (Math.random() - 0.5) * 150);
}
return true;
}
return false;
};
self.update = function () {
var dy = 1366 - self.y;
var distance = Math.abs(dy);
if (distance > 300) {
self.y += self.speed;
self.x = self.startX + Math.sin(LK.ticks * 0.05) * 400;
} else {
self.x = self.startX + Math.sin(LK.ticks * 0.05) * 400;
}
if (self.specialAttackCooldown <= 0) {
for (var i = 0; i < 4; i++) {
var miniPigeon = new CyberPigeon();
miniPigeon.x = self.x + (i - 1.5) * 50; // Fila horizontal, espaciado de 100 píxeles
miniPigeon.y = self.y;
miniPigeon.health = 20;
cyberPigeons.push(miniPigeon);
game.addChild(miniPigeon);
}
self.specialAttackCooldown = 180;
} else {
self.specialAttackCooldown--;
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
pigeonGraphics.rotation = Math.atan2(dy, 1024 - self.x);
};
return self;
});
var CyberPigeon = Container.expand(function () {
var self = Container.call(this);
var type = Math.random() < 0.15 ? 'rotating' : Math.random() < 0.8235 ? 'normal' : 'fast';
var assetName = type === 'fast' ? 'fastPigeon' : type === 'rotating' ? 'rotatingPigeon' : 'cyberPigeon';
var pigeonGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.health = Math.min(35 + 4 * Math.sqrt(wave), 75);
self.maxHealth = self.health;
self.speed = Math.min(1.8 + 0.3 * Math.sqrt(wave), 8);
self.attackCooldown = 0;
self.type = type;
if (self.type === 'fast') {
self.speed *= 1.8;
self.health *= 0.6;
} else if (self.type === 'rotating') {
self.speed *= 1.2;
self.health *= 0.8;
self.orbitRadius = 50;
self.orbitAngle = Math.random() * Math.PI * 2;
self.orbitSpeed = 0.1;
}
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 150);
var damageText = new Text2('-' + damage, {
size: 40,
fill: 0xff0000
});
damageText.x = self.x;
damageText.y = self.y - 30;
game.addChild(damageText);
tween(damageText, {
y: damageText.y - 60,
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
damageText.destroy();
}
});
if (self.health <= 0) {
score += self.type === 'fast' ? 20 : self.type === 'rotating' ? 25 : 15;
var explosion = new Explosion();
explosion.x = self.x;
explosion.y = self.y;
game.addChild(explosion);
explosions.push(explosion);
LK.getSound('explosion').play();
return true;
}
return false;
};
self.update = function () {
var dx = 1024 - self.x;
var dy = 1366 - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
if (self.type === 'rotating') {
self.orbitAngle += self.orbitSpeed;
var orbitX = dx / distance * self.speed;
var orbitY = dy / distance * self.speed;
self.x += orbitX + Math.cos(self.orbitAngle) * self.orbitRadius * 0.1;
self.y += orbitY + Math.sin(self.orbitAngle) * self.orbitRadius * 0.1;
} else {
var wobble = self.type === 'fast' ? Math.sin(LK.ticks * 0.25) * 25 : 0;
self.x += dx / distance * self.speed + Math.sin(LK.ticks * 0.12) * 0.6;
self.y += dy / distance * self.speed + wobble * 0.12;
}
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
pigeonGraphics.rotation = Math.atan2(dy, dx);
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 35;
self.radius = 100;
explosionGraphics.scaleX = 0.2;
explosionGraphics.scaleY = 0.2;
self.update = function () {
self.lifeTime--;
var progress = 1 - self.lifeTime / 35;
explosionGraphics.scaleX = 0.2 + progress * 2.8;
explosionGraphics.scaleY = 0.2 + progress * 2.8;
explosionGraphics.alpha = 1 - progress;
explosionGraphics.rotation += 0.15;
return self.lifeTime <= 0;
};
return self;
});
var Grandma = Container.expand(function () {
var self = Container.call(this);
var grandmaGraphics = self.attachAsset('grandma', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 120;
self.maxHealth = 120;
self.bombDamage = 60;
self.shotCooldown = 0;
self.speedBoost = 0;
self.damageBoost = 0;
self.multishot = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
game.x = (Math.random() - 0.5) * 25;
game.y = (Math.random() - 0.5) * 25;
LK.setTimeout(function () {
game.x = 0;
game.y = 0;
}, 120);
if (self.health <= 0) {
LK.showGameOver();
}
};
self.update = function () {
self.x = 1024;
self.y = 1366;
if (self.health < self.maxHealth && LK.ticks % (wave > 10 ? 400 : 500) === 0) {
self.health = Math.min(self.health + (wave > 10 ? 3 : 2), self.maxHealth);
}
if (self.shotCooldown > 0) {
self.shotCooldown--;
}
};
return self;
});
var KetchupBomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('ketchupBomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifeTime = 80;
self.damage = grandma.bombDamage + grandma.damageBoost;
self.trail = [];
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.lifeTime--;
bombGraphics.rotation += 0.25;
self.trail.push({
x: self.x,
y: self.y,
alpha: 1
});
if (self.trail.length > 6) {
self.trail.shift();
}
if (self.lifeTime <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var rand = Math.random();
var types = rand < 0.1 ? 'multishot' : rand < 0.4 ? 'speed' : rand < 0.7 ? 'health' : 'damage';
self.type = types;
var assetName = 'powerUp' + self.type.charAt(0).toUpperCase() + self.type.slice(1);
var powerUpGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 800;
self.update = function () {
self.lifeTime--;
self.y += Math.sin(LK.ticks * 0.18) * 1.2;
powerUpGraphics.rotation += 0.001;
powerUpGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.12) * 0.25;
powerUpGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.12) * 0.25;
if (self.lifeTime < 160) {
powerUpGraphics.alpha = Math.sin(LK.ticks * 0.35) * 0.5 + 0.5;
}
return self.lifeTime <= 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var background = game.addChild(LK.getAsset('background', {
x: 0,
y: 0
}));
/****
* Game Variables
****/
var grandma;
var cyberPigeons = [];
var ketchupBombs = [];
var explosions = [];
var powerUps = [];
var score = 0;
var wave = 1;
var enemiesKilled = 0;
var enemiesThisWave = 0;
var totalEnemiesThisWave = 0;
var lastClickTime = 0;
var comboCounter = 0;
var comboTimer = 0;
var bossWave = false;
/****
* UI Elements
****/
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
scoreText.x = 50;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
var healthText = new Text2('Health: 120/120', {
size: 50,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 3
});
healthText.anchor.set(1, 0);
LK.gui.topRight.addChild(healthText);
var waveText = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 3
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var bossHealthBg = LK.getAsset('chargeBarBg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
x: 0,
y: 100
});
LK.gui.top.addChild(bossHealthBg);
bossHealthBg.visible = false;
var bossHealthBar = LK.getAsset('chargeBar', {
anchorX: 0,
anchorY: 0,
scaleX: 0,
x: -150,
y: 100
});
LK.gui.top.addChild(bossHealthBar);
bossHealthBar.visible = false;
var comboText = new Text2('', {
size: 60,
fill: 0x00FFFF,
stroke: 0x000000,
strokeThickness: 3
});
comboText.anchor.set(0.5, 0);
comboText.x = 1024;
comboText.y = 200;
game.addChild(comboText);
var waveProgressBg = LK.getAsset('chargeBarBg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
x: 0,
y: 60
});
LK.gui.top.addChild(waveProgressBg);
var waveProgressBar = LK.getAsset('chargeBar', {
anchorX: 0,
anchorY: 0,
scaleX: 0,
x: -150,
y: 60
});
LK.gui.top.addChild(waveProgressBar);
/****
* Game Functions
****/
function createGrandma() {
grandma = game.addChild(new Grandma());
grandma.x = 1024;
grandma.y = 1366;
}
function throwKetchupBomb(targetX, targetY) {
if (grandma.shotCooldown > 0) {
return;
}
LK.getSound('attack').play();
LK.effects.flashObject(grandma, 0xffff00, 150);
var shots = grandma.multishot > 0 ? 3 : 1;
var angles = [0];
if (shots > 1) {
angles = [-15, 0, 15];
}
var enemiesHit = 0;
for (var i = 0; i < shots; i++) {
var bomb = new KetchupBomb();
bomb.x = grandma.x;
bomb.y = grandma.y;
var angle = Math.atan2(targetY - grandma.y, targetX - grandma.x) + angles[i] * Math.PI / 180;
var dx = Math.cos(angle);
var dy = Math.sin(angle);
var speed = 12;
bomb.velocityX = dx * speed;
bomb.velocityY = dy * speed;
bomb.damage = grandma.bombDamage + grandma.damageBoost;
ketchupBombs.push(bomb);
game.addChild(bomb);
enemiesHit += checkImmediateHits(bomb, angle);
}
if (enemiesHit > 0) {
comboCounter += enemiesHit;
comboTimer = 150;
if (comboCounter >= 3) {
score += comboCounter * 6;
}
}
grandma.shotCooldown = 24 - Math.min(grandma.speedBoost * 2, 16);
}
function checkImmediateHits(bomb, angle) {
var enemiesHit = 0;
var range = 300;
for (var j = cyberPigeons.length - 1; j >= 0; j--) {
var pigeon = cyberPigeons[j];
var pdx = pigeon.x - grandma.x;
var pdy = pigeon.y - grandma.y;
var distance = Math.sqrt(pdx * pdx + pdy * pdy);
if (distance < range && Math.abs(Math.atan2(pdy, pdx) - angle) < 0.3) {
if (pigeon.takeDamage(bomb.damage)) {
pigeon.destroy();
cyberPigeons.splice(j, 1);
enemiesKilled++;
enemiesThisWave++;
enemiesHit++;
}
}
}
for (var j = powerUps.length - 1; j >= 0; j--) {
var powerUp = powerUps[j];
var pdx = powerUp.x - grandma.x;
var pdy = powerUp.y - grandma.y;
var distance = Math.sqrt(pdx * pdx + pdy * pdy);
if (distance < range && Math.abs(Math.atan2(pdy, pdx) - angle) < 0.3) {
collectPowerUp(powerUp);
powerUp.destroy();
powerUps.splice(j, 1);
}
}
return enemiesHit;
}
function spawnEnemies() {
bossWave = wave % 5 === 0;
if (bossWave) {
var boss = new BossPigeon();
boss.x = 1024;
boss.y = -150;
cyberPigeons.push(boss);
game.addChild(boss);
totalEnemiesThisWave = 1;
} else {
var maxEnemies = Math.min(15 + Math.floor(wave / 5), 30);
var enemiesToSpawn = Math.min(4 + wave, maxEnemies);
totalEnemiesThisWave = enemiesToSpawn;
for (var i = 0; i < enemiesToSpawn; i++) {
LK.setTimeout(function () {
var pigeon = new CyberPigeon();
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
pigeon.x = Math.random() * 2048;
pigeon.y = -50;
break;
case 1:
pigeon.x = 2098;
pigeon.y = Math.random() * 2732;
break;
case 2:
pigeon.x = Math.random() * 2048;
pigeon.y = 2782;
break;
case 3:
pigeon.x = -50;
pigeon.y = Math.random() * 2732;
break;
}
cyberPigeons.push(pigeon);
game.addChild(pigeon);
}, i * (wave > 10 ? 300 : 400));
}
}
enemiesThisWave = 0;
}
function spawnPowerUpAt(x, y) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
function collectPowerUp(powerUp) {
LK.getSound('powerup').play();
LK.effects.flashObject(grandma, 0x00ff00, 400);
var bonusText = new Text2('', {
size: 50,
fill: 0x00ff00
});
bonusText.x = grandma.x;
bonusText.y = grandma.y - 60;
game.addChild(bonusText);
switch (powerUp.type) {
case 'speed':
grandma.speedBoost += 2;
bonusText.setText('FIRE RATE UP!');
break;
case 'health':
grandma.health = Math.min(grandma.health + 30 + 2 * wave, grandma.maxHealth);
bonusText.setText('HEALTH +' + (30 + 2 * wave) + '!');
break;
case 'damage':
grandma.damageBoost += 12;
bonusText.setText('DAMAGE UP!');
break;
case 'multishot':
grandma.multishot = 180;
bonusText.setText('MULTISHOT!');
break;
}
tween(bonusText, {
y: bonusText.y - 100,
alpha: 0
}, {
duration: 1200,
onComplete: function onComplete() {
bonusText.destroy();
}
});
}
/****
* Event Handlers
****/
game.down = function (x, y, obj) {
throwKetchupBomb(x, y);
};
/****
* Game Loop
****/
createGrandma();
LK.playMusic('gameMusic');
spawnEnemies();
game.update = function () {
if (comboTimer > 0) {
comboTimer--;
if (comboCounter >= 3) {
comboText.setText('COMBO x' + comboCounter + '!');
} else {
comboText.setText('');
}
} else {
comboCounter = 0;
comboText.setText('');
}
for (var i = cyberPigeons.length - 1; i >= 0; i--) {
var pigeon = cyberPigeons[i];
pigeon.update();
if (pigeon.intersects(grandma) && pigeon.attackCooldown <= 0) {
var damage = pigeon.isBoss ? 20 : 10;
grandma.takeDamage(damage);
pigeon.attackCooldown = 50;
}
if (pigeon.x < -200 || pigeon.x > 2248 || pigeon.y < -200 || pigeon.y > 2932) {
pigeon.destroy();
cyberPigeons.splice(i, 1);
}
}
for (var i = ketchupBombs.length - 1; i >= 0; i--) {
var bomb = ketchupBombs[i];
var shouldRemove = bomb.update();
if (shouldRemove || bomb.x < -100 || bomb.x > 2148 || bomb.y < -100 || bomb.y > 2832) {
bomb.destroy();
ketchupBombs.splice(i, 1);
} else {
for (var j = cyberPigeons.length - 1; j >= 0; j--) {
var pigeon = cyberPigeons[j];
if (bomb.intersects(pigeon)) {
if (pigeon.takeDamage(bomb.damage)) {
pigeon.destroy();
cyberPigeons.splice(j, 1);
enemiesKilled++;
enemiesThisWave++;
}
bomb.destroy();
ketchupBombs.splice(i, 1);
break;
}
}
for (var j = powerUps.length - 1; j >= 0; j--) {
var powerUp = powerUps[j];
if (bomb.intersects(powerUp)) {
collectPowerUp(powerUp);
powerUp.destroy();
powerUps.splice(j, 1);
bomb.destroy();
ketchupBombs.splice(i, 1);
break;
}
}
}
}
for (var i = explosions.length - 1; i >= 0; i--) {
var explosion = explosions[i];
var shouldRemove = explosion.update();
if (shouldRemove) {
explosion.destroy();
explosions.splice(i, 1);
}
}
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
var shouldRemove = powerUp.update();
if (shouldRemove) {
powerUp.destroy();
powerUps.splice(i, 1);
}
}
if (grandma.multishot > 0) {
grandma.multishot--;
}
grandma.update();
if (cyberPigeons.length === 0) {
wave++;
spawnEnemies();
if (Math.random() < 1) {
spawnPowerUpAt(Math.random() * 1600 + 200, Math.random() * 2200 + 200);
}
}
scoreText.setText('Score: ' + score);
healthText.setText('Health: ' + grandma.health + '/' + grandma.maxHealth);
waveText.setText('Wave: ' + wave + (bossWave ? ' (BOSS)' : ''));
// Actualizar barra de vida del jefe
if (bossWave && cyberPigeons.length > 0) {
var boss = cyberPigeons.find(function (p) {
return p.isBoss;
});
if (boss) {
bossHealthBg.visible = true;
bossHealthBar.visible = true;
bossHealthBar.scaleX = boss.health / boss.maxHealth * 1.5;
}
} else {
bossHealthBg.visible = false;
bossHealthBar.visible = false;
}
var progress = totalEnemiesThisWave > 0 ? enemiesThisWave / totalEnemiesThisWave : 0;
waveProgressBar.scaleX = progress * 1.5;
LK.setScore(score);
};
Pixel art sprite of a fierce old lady wearing sunglasses and a scarf, riding a rocket-powered shopping cart. She’s holding a glowing cane like a weapon. Side view, colorful retro 2D style. PNG. In-Game asset. 2d. High contrast. No shadows
Pixel art of an evil pigeon with robotic parts, red glowing eyes, and tiny armor. Flying pose, side view, 2D pixel sprite for an arcade game. In-Game asset. 2d. High contrast. No shadows
Pixel art of a red ketchup bottle turned into a bomb, with a fuse and tomato sauce leaking out. Small item, cartoonish and fun, pixel style. In-Game asset. 2d. High contrast. No shadows
pixel art dog. In-Game asset. 2d. High contrast. No shadows
explosion. In-Game asset. 2d. High contrast. No shadows
background pixel art cielo azul. In-Game asset. 2d. High contrast. No shadows
Pixel art of a smaller robotic pigeon with jet wings and a streamlined shape. Fast enemy type in a 2D arcade game. Side view, green metal feathers. In-Game asset. 2d. High contrast. No shadows
Pixel art of a purple power-up icon with a spiked fist symbol or explosion inside. Represents bonus attack damage in a retro game. In-Game asset. 2d. High contrast. No shadows
Pixel art of a dark purple robotic pigeon spinning in midair, wings blurred in motion. Enemy type in a 2D arcade game, side view. In-Game asset. 2d. High contrast. No shadows
Pixel art of a green power-up icon with a heart or medical cross symbol inside. Represents healing in a 2D pixel arcade game. Glowing green, square icon. In-Game asset. 2d. High contrast. No shadows
Pixel art of a red power-up icon with a bold lightning bolt symbol inside. Represents a temporary speed boost in a retro-style arcade game. Glowing red, square icon. In-Game asset. 2d. High contrast. No shadows
Pixel art of a violet power-up icon with three small arrows or projectiles spreading outward. Represents multishot power in a retro-style game. Glowing, pixelated square icon. In-Game asset. 2d. High contrast. No shadows