/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Broccoli enemy class
var Broccoli = Container.expand(function () {
var self = Container.call(this);
var broccoliAsset = self.attachAsset('broccoli', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Moves down, will be randomized per instance
self.update = function () {
self.y += self.speed;
};
return self;
});
// Candy projectile class
var Candy = Container.expand(function () {
var self = Container.call(this);
var candyAsset = self.attachAsset('candy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32; // Moves up
self.update = function () {
self.y += self.speed;
};
return self;
});
// Cannon class
var Cannon = Container.expand(function () {
var self = Container.call(this);
var cannonAsset = self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Sound for shooting
// Cannon (box, purple)
// Broccoli enemy (ellipse, green)
// Candy projectile (ellipse, pink)
// Game area: 2048x2732
// Cannon position: bottom center, above bottom edge
var cannon = new Cannon();
cannon.x = 2048 / 2;
cannon.y = 2732 - 180;
game.addChild(cannon);
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Shield status display
var shieldTxt = new Text2('', {
size: 80,
fill: 0x00FFFF
});
shieldTxt.anchor.set(1, 0); // Top right
LK.gui.topRight.addChild(shieldTxt);
// Update shield status text every frame
LK.setInterval(function () {
if (shieldActive) {
shieldTxt.setText('🛡️ ' + Math.ceil(shieldTimer / 60));
} else {
shieldTxt.setText('');
}
}, 100);
// Arrays to track candies and broccoli
var candies = [];
var broccolis = [];
// --- Shield Lollipop Powerup State ---
var shieldActive = false;
var shieldTimer = 0;
var shieldDuration = 300; // 5 seconds at 60fps
// For drag-to-aim
var isAiming = false;
var aimX = cannon.x;
// Prevent cannon from moving into top left 100x100
var minCannonX = 180 / 2 + 100;
var maxCannonX = 2048 - 180 / 2;
// Handle aiming and firing
function handleMove(x, y, obj) {
if (isAiming) {
// Clamp aimX to stay within screen and not overlap top left
aimX = Math.max(minCannonX, Math.min(maxCannonX, x));
cannon.x = aimX;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow firing if touch/click is on or near the cannon (bottom 400px)
if (y > 2732 - 400) {
isAiming = true;
aimX = Math.max(minCannonX, Math.min(maxCannonX, x));
cannon.x = aimX;
// Fire immediately on down
fireCandy();
}
};
game.up = function (x, y, obj) {
isAiming = false;
};
// Fire a candy from the cannon's current position
function fireCandy() {
var candy = new Candy();
candy.x = cannon.x;
candy.y = cannon.y - 80; // Appear just above the cannon
candies.push(candy);
game.addChild(candy);
LK.getSound('shoot').play();
}
// --- Automatic shooting timer ---
var autoShootInterval = 36; // frames between shots (about 1.6 shots/sec at 60fps)
var autoShootTimer = 0;
// Broccoli spawn timer
var broccoliSpawnInterval = 60; // frames (1s at 60fps)
var broccoliSpeedMin = 8;
var broccoliSpeedMax = 16;
var broccoliSpawnTimer = 0;
// Difficulty ramping
function getBroccoliSpeed() {
// Increase speed as score increases
var base = broccoliSpeedMin + Math.floor(LK.getScore() / 10) * 2;
return Math.min(base + Math.random() * 4, broccoliSpeedMax + LK.getScore() * 0.5);
}
function getBroccoliInterval() {
// Decrease interval as score increases
return Math.max(20, broccoliSpawnInterval - Math.floor(LK.getScore() / 5) * 5);
}
// Main game update loop
game.update = function () {
// Update candies
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
candy.update();
// Remove if off screen
if (candy.y < -100) {
candy.destroy();
candies.splice(i, 1);
}
}
// Update broccolis
for (var j = broccolis.length - 1; j >= 0; j--) {
var broccoli = broccolis[j];
broccoli.update();
// Check for collision with any candy
var hit = false;
for (var k = candies.length - 1; k >= 0; k--) {
var candy = candies[k];
if (broccoli.intersects(candy)) {
// --- Shield Lollipop Drop Chance ---
// 1 in 10 chance to drop shield when broccoli is destroyed
if (!shieldActive && Math.random() < 0.1) {
shieldActive = true;
shieldTimer = shieldDuration;
// Optional: flash cannon or show effect
LK.effects.flashObject(cannon, 0x00ffff, 500);
}
// Destroy both
broccoli.destroy();
candy.destroy();
broccolis.splice(j, 1);
candies.splice(k, 1);
// Score up
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
hit = true;
break;
}
}
if (hit) continue;
// Check for reaching cannon (game over or shield)
if (broccoli.y + 60 >= cannon.y - 50 && Math.abs(broccoli.x - cannon.x) < 120) {
if (shieldActive) {
// Block game over, consume shield, destroy broccoli, flash blue
shieldActive = false;
shieldTimer = 0;
LK.effects.flashScreen(0x00ffff, 500);
broccoli.destroy();
broccolis.splice(j, 1);
continue;
} else {
// Flash screen red
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Remove if off screen (bottom)
if (broccoli.y > 2732 + 100) {
broccoli.destroy();
broccolis.splice(j, 1);
}
}
// Spawn broccoli
broccoliSpawnTimer++;
if (broccoliSpawnTimer >= getBroccoliInterval()) {
broccoliSpawnTimer = 0;
var broccoli = new Broccoli();
// Random x, avoid top left 100px
var bx = 100 + 60 + Math.random() * (2048 - 200);
broccoli.x = bx;
broccoli.y = -60;
broccoli.speed = getBroccoliSpeed();
broccolis.push(broccoli);
game.addChild(broccoli);
}
// --- Automatic shooting logic ---
autoShootTimer++;
if (autoShootTimer >= autoShootInterval) {
autoShootTimer = 0;
fireCandy();
}
// --- Shield Lollipop Timer ---
if (shieldActive) {
shieldTimer--;
if (shieldTimer <= 0) {
shieldActive = false;
shieldTimer = 0;
}
}
};
// Initial score
LK.setScore(0);
scoreTxt.setText('0'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Broccoli enemy class
var Broccoli = Container.expand(function () {
var self = Container.call(this);
var broccoliAsset = self.attachAsset('broccoli', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Moves down, will be randomized per instance
self.update = function () {
self.y += self.speed;
};
return self;
});
// Candy projectile class
var Candy = Container.expand(function () {
var self = Container.call(this);
var candyAsset = self.attachAsset('candy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32; // Moves up
self.update = function () {
self.y += self.speed;
};
return self;
});
// Cannon class
var Cannon = Container.expand(function () {
var self = Container.call(this);
var cannonAsset = self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Sound for shooting
// Cannon (box, purple)
// Broccoli enemy (ellipse, green)
// Candy projectile (ellipse, pink)
// Game area: 2048x2732
// Cannon position: bottom center, above bottom edge
var cannon = new Cannon();
cannon.x = 2048 / 2;
cannon.y = 2732 - 180;
game.addChild(cannon);
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Shield status display
var shieldTxt = new Text2('', {
size: 80,
fill: 0x00FFFF
});
shieldTxt.anchor.set(1, 0); // Top right
LK.gui.topRight.addChild(shieldTxt);
// Update shield status text every frame
LK.setInterval(function () {
if (shieldActive) {
shieldTxt.setText('🛡️ ' + Math.ceil(shieldTimer / 60));
} else {
shieldTxt.setText('');
}
}, 100);
// Arrays to track candies and broccoli
var candies = [];
var broccolis = [];
// --- Shield Lollipop Powerup State ---
var shieldActive = false;
var shieldTimer = 0;
var shieldDuration = 300; // 5 seconds at 60fps
// For drag-to-aim
var isAiming = false;
var aimX = cannon.x;
// Prevent cannon from moving into top left 100x100
var minCannonX = 180 / 2 + 100;
var maxCannonX = 2048 - 180 / 2;
// Handle aiming and firing
function handleMove(x, y, obj) {
if (isAiming) {
// Clamp aimX to stay within screen and not overlap top left
aimX = Math.max(minCannonX, Math.min(maxCannonX, x));
cannon.x = aimX;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow firing if touch/click is on or near the cannon (bottom 400px)
if (y > 2732 - 400) {
isAiming = true;
aimX = Math.max(minCannonX, Math.min(maxCannonX, x));
cannon.x = aimX;
// Fire immediately on down
fireCandy();
}
};
game.up = function (x, y, obj) {
isAiming = false;
};
// Fire a candy from the cannon's current position
function fireCandy() {
var candy = new Candy();
candy.x = cannon.x;
candy.y = cannon.y - 80; // Appear just above the cannon
candies.push(candy);
game.addChild(candy);
LK.getSound('shoot').play();
}
// --- Automatic shooting timer ---
var autoShootInterval = 36; // frames between shots (about 1.6 shots/sec at 60fps)
var autoShootTimer = 0;
// Broccoli spawn timer
var broccoliSpawnInterval = 60; // frames (1s at 60fps)
var broccoliSpeedMin = 8;
var broccoliSpeedMax = 16;
var broccoliSpawnTimer = 0;
// Difficulty ramping
function getBroccoliSpeed() {
// Increase speed as score increases
var base = broccoliSpeedMin + Math.floor(LK.getScore() / 10) * 2;
return Math.min(base + Math.random() * 4, broccoliSpeedMax + LK.getScore() * 0.5);
}
function getBroccoliInterval() {
// Decrease interval as score increases
return Math.max(20, broccoliSpawnInterval - Math.floor(LK.getScore() / 5) * 5);
}
// Main game update loop
game.update = function () {
// Update candies
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
candy.update();
// Remove if off screen
if (candy.y < -100) {
candy.destroy();
candies.splice(i, 1);
}
}
// Update broccolis
for (var j = broccolis.length - 1; j >= 0; j--) {
var broccoli = broccolis[j];
broccoli.update();
// Check for collision with any candy
var hit = false;
for (var k = candies.length - 1; k >= 0; k--) {
var candy = candies[k];
if (broccoli.intersects(candy)) {
// --- Shield Lollipop Drop Chance ---
// 1 in 10 chance to drop shield when broccoli is destroyed
if (!shieldActive && Math.random() < 0.1) {
shieldActive = true;
shieldTimer = shieldDuration;
// Optional: flash cannon or show effect
LK.effects.flashObject(cannon, 0x00ffff, 500);
}
// Destroy both
broccoli.destroy();
candy.destroy();
broccolis.splice(j, 1);
candies.splice(k, 1);
// Score up
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
hit = true;
break;
}
}
if (hit) continue;
// Check for reaching cannon (game over or shield)
if (broccoli.y + 60 >= cannon.y - 50 && Math.abs(broccoli.x - cannon.x) < 120) {
if (shieldActive) {
// Block game over, consume shield, destroy broccoli, flash blue
shieldActive = false;
shieldTimer = 0;
LK.effects.flashScreen(0x00ffff, 500);
broccoli.destroy();
broccolis.splice(j, 1);
continue;
} else {
// Flash screen red
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Remove if off screen (bottom)
if (broccoli.y > 2732 + 100) {
broccoli.destroy();
broccolis.splice(j, 1);
}
}
// Spawn broccoli
broccoliSpawnTimer++;
if (broccoliSpawnTimer >= getBroccoliInterval()) {
broccoliSpawnTimer = 0;
var broccoli = new Broccoli();
// Random x, avoid top left 100px
var bx = 100 + 60 + Math.random() * (2048 - 200);
broccoli.x = bx;
broccoli.y = -60;
broccoli.speed = getBroccoliSpeed();
broccolis.push(broccoli);
game.addChild(broccoli);
}
// --- Automatic shooting logic ---
autoShootTimer++;
if (autoShootTimer >= autoShootInterval) {
autoShootTimer = 0;
fireCandy();
}
// --- Shield Lollipop Timer ---
if (shieldActive) {
shieldTimer--;
if (shieldTimer <= 0) {
shieldActive = false;
shieldTimer = 0;
}
}
};
// Initial score
LK.setScore(0);
scoreTxt.setText('0');