/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -15;
self.power = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.moveDirection = 1; // 1 for right, -1 for left
self.moveDistance = 0;
self.maxDistance = 200; // Maximum horizontal movement
self.descendAmount = 50; // Amount to descend after changing direction
self.hit = function (damage) {
self.health -= damage;
if (self.health <= 0) {
return true; // Enemy is destroyed
}
// Visual feedback for hit
tween(enemyGraphics, {
tint: 0xff0000
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xffffff
}, {
duration: 100,
easing: tween.linear
});
}
});
return false; // Enemy still alive
};
self.update = function () {
// Move horizontally
self.x += self.speed * self.moveDirection;
self.moveDistance += Math.abs(self.speed);
// Change direction and descend when reached max distance
if (self.moveDistance >= self.maxDistance) {
self.moveDirection *= -1; // Reverse direction
self.y += self.descendAmount; // Move down
self.moveDistance = 0; // Reset distance counter
}
// Simple rotation animation
self.rotation = Math.sin(LK.ticks / 30) * 0.1;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.canFire = true;
self.fireDelay = 300;
self.lastFired = 0;
self.specialWeapon = false;
self.specialTimer = 0;
self.lives = 3;
self.fire = function () {
var currentTime = Date.now();
if (self.canFire && currentTime - self.lastFired > self.fireDelay) {
var bullet;
if (self.specialWeapon) {
bullet = new SpecialBullet();
} else {
bullet = new Bullet();
}
bullet.x = self.x;
bullet.y = self.y - 60;
game.addChild(bullet);
bullets.push(bullet);
LK.getSound('laser').play();
self.lastFired = currentTime;
}
};
self.activateSpecialWeapon = function () {
self.specialWeapon = true;
self.specialTimer = 5000; // Special weapon lasts for 5 seconds
// Visual feedback for special weapon
tween(shipGraphics, {
tint: 0xffff00
}, {
duration: 300,
easing: tween.easeOut
});
};
self.update = function () {
// Update special weapon timer
if (self.specialWeapon && self.specialTimer > 0) {
self.specialTimer -= 16.67; // Approximate for 60fps
if (self.specialTimer <= 0) {
self.specialWeapon = false;
tween(shipGraphics, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.type = "special"; // Default type
self.update = function () {
self.y += self.speed;
self.rotation += 0.03;
};
return self;
});
var SpecialBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('specialBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -18;
self.power = 3;
self.radius = 25;
self.update = function () {
self.y += self.speed;
self.rotation += 0.1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
// Game variables
var player;
var bullets = [];
var enemies = [];
var powerUps = [];
var gameStarted = false;
var gameOver = false;
var score = 0;
var level = 1;
var enemiesPerRow = 5;
var enemyRows = 3;
var enemySpawnDelay = 800;
var lastEnemySpawn = 0;
var powerUpChance = 0.2;
var highScore = storage.highScore || 0;
// UI Elements
var scoreTxt = new Text2('SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
var levelTxt = new Text2('LEVEL: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 20;
var instructionsTxt = new Text2('Open mouth to FIRE\nMake noise for SPECIAL ATTACK', {
size: 40,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(instructionsTxt);
instructionsTxt.y = -100;
var livesTxt = new Text2('LIVES: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(livesTxt);
livesTxt.x = 120; // Keep away from top-left corner (menu icon)
livesTxt.y = 20;
// Start the game
function startGame() {
if (!gameStarted) {
gameStarted = true;
// Initialize player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 200;
game.addChild(player);
// Reset game state
score = 0;
level = 1;
bullets = [];
enemies = [];
powerUps = [];
// Start background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
// Start spawning enemies
spawnEnemyWave();
}
}
// Spawn wave of enemies
function spawnEnemyWave() {
var startX = (2048 - enemiesPerRow * 150) / 2 + 75;
var startY = 200;
for (var row = 0; row < enemyRows; row++) {
for (var col = 0; col < enemiesPerRow; col++) {
var enemy = new Enemy();
enemy.x = startX + col * 150;
enemy.y = startY + row * 120;
enemy.health = Math.ceil(level / 3); // Increase health every 3 levels
enemy.speed = 1 + level * 0.5; // Increase speed with level
// Alternate movement direction by row
if (row % 2 === 1) {
enemy.moveDirection = -1;
}
game.addChild(enemy);
enemies.push(enemy);
}
}
}
// Spawn a power-up
function spawnPowerUp(x, y) {
if (Math.random() < powerUpChance) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
game.addChild(powerUp);
powerUps.push(powerUp);
}
}
// Update score display
function updateScoreDisplay() {
scoreTxt.setText("SCORE: " + score);
levelTxt.setText("LEVEL: " + level);
livesTxt.setText("LIVES: " + player.lives);
}
// Check for level completion
function checkLevelComplete() {
if (enemies.length === 0) {
level++;
enemiesPerRow = Math.min(enemiesPerRow + 1, 8);
enemyRows = Math.min(enemyRows + 1, 5);
powerUpChance = Math.min(powerUpChance + 0.05, 0.5);
// Clear all bullets
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
// Spawn new enemy wave
spawnEnemyWave();
// Update level display
updateScoreDisplay();
}
}
// Check for game over conditions
function checkGameOver() {
// Check if player has no lives left
if (player.lives <= 0) {
endGame();
return;
}
// Check if any enemies have reached the bottom
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y > 2732 - 150) {
endGame();
return;
}
}
}
// End game function
function endGame() {
gameOver = true;
gameStarted = false;
// Check for high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Show game over screen
LK.showGameOver();
// Stop music
LK.stopMusic();
}
// Main update function
game.update = function () {
// Start game if face is detected and game not started
if (facekit.mouthCenter && !gameStarted && !gameOver) {
startGame();
}
if (gameStarted && !gameOver) {
// Update player position based on face position
if (facekit.mouthCenter) {
var targetX = facekit.mouthCenter.x;
player.x += (targetX - player.x) * 0.2; // Smooth movement
// Fire weapon if mouth is open
if (facekit.mouthOpen) {
player.fire();
}
// Activate special weapon if making noise
if (facekit.volume > 0.3 && !player.specialWeapon) {
player.activateSpecialWeapon();
}
}
// Update player
player.update();
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Update bullet position
bullet.update();
// Remove bullets that go off screen
if (bullet.y < -100) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check for collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
var destroyed = enemy.hit(bullet.power);
if (destroyed) {
// Add score
score += 10 * level;
// Play explosion sound
LK.getSound('explosion').play();
// Flash effect for destroyed enemy
LK.effects.flashObject(enemy, 0xff0000, 200);
// Possibly spawn power-up
spawnPowerUp(enemy.x, enemy.y);
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
}
// Remove bullet
bullet.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
// Check for collision with player
if (enemies[i].intersects(player)) {
player.lives--;
// Flash screen red
LK.effects.flashScreen(0xff0000, 300);
// Remove enemy
enemies[i].destroy();
enemies.splice(i, 1);
i--;
// Check if game over
if (player.lives <= 0) {
checkGameOver();
} else {
updateScoreDisplay();
}
continue;
}
}
// Update power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
// Update power-up position
powerUp.update();
// Remove power-ups that go off screen
if (powerUp.y > 2732 + 100) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check for collision with player
if (powerUp.intersects(player)) {
// Apply power-up effect
player.activateSpecialWeapon();
// Play power-up sound
LK.getSound('powerup').play();
// Remove power-up
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Check if level is complete
checkLevelComplete();
// Check for game over conditions
checkGameOver();
// Update UI
updateScoreDisplay();
}
};
// Event handlers
game.down = function (x, y, obj) {
// Touch to start game
if (!gameStarted && !gameOver) {
startGame();
}
};
// Start with an instructional message
var startText = new Text2('Face the camera and\nopen your mouth to start!', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2;
game.addChild(startText);
// Remove start text when game starts
var checkStarted = LK.setInterval(function () {
if (gameStarted) {
startText.destroy();
LK.clearInterval(checkStarted);
}
}, 100); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -15;
self.power = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.moveDirection = 1; // 1 for right, -1 for left
self.moveDistance = 0;
self.maxDistance = 200; // Maximum horizontal movement
self.descendAmount = 50; // Amount to descend after changing direction
self.hit = function (damage) {
self.health -= damage;
if (self.health <= 0) {
return true; // Enemy is destroyed
}
// Visual feedback for hit
tween(enemyGraphics, {
tint: 0xff0000
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xffffff
}, {
duration: 100,
easing: tween.linear
});
}
});
return false; // Enemy still alive
};
self.update = function () {
// Move horizontally
self.x += self.speed * self.moveDirection;
self.moveDistance += Math.abs(self.speed);
// Change direction and descend when reached max distance
if (self.moveDistance >= self.maxDistance) {
self.moveDirection *= -1; // Reverse direction
self.y += self.descendAmount; // Move down
self.moveDistance = 0; // Reset distance counter
}
// Simple rotation animation
self.rotation = Math.sin(LK.ticks / 30) * 0.1;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.canFire = true;
self.fireDelay = 300;
self.lastFired = 0;
self.specialWeapon = false;
self.specialTimer = 0;
self.lives = 3;
self.fire = function () {
var currentTime = Date.now();
if (self.canFire && currentTime - self.lastFired > self.fireDelay) {
var bullet;
if (self.specialWeapon) {
bullet = new SpecialBullet();
} else {
bullet = new Bullet();
}
bullet.x = self.x;
bullet.y = self.y - 60;
game.addChild(bullet);
bullets.push(bullet);
LK.getSound('laser').play();
self.lastFired = currentTime;
}
};
self.activateSpecialWeapon = function () {
self.specialWeapon = true;
self.specialTimer = 5000; // Special weapon lasts for 5 seconds
// Visual feedback for special weapon
tween(shipGraphics, {
tint: 0xffff00
}, {
duration: 300,
easing: tween.easeOut
});
};
self.update = function () {
// Update special weapon timer
if (self.specialWeapon && self.specialTimer > 0) {
self.specialTimer -= 16.67; // Approximate for 60fps
if (self.specialTimer <= 0) {
self.specialWeapon = false;
tween(shipGraphics, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.type = "special"; // Default type
self.update = function () {
self.y += self.speed;
self.rotation += 0.03;
};
return self;
});
var SpecialBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('specialBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -18;
self.power = 3;
self.radius = 25;
self.update = function () {
self.y += self.speed;
self.rotation += 0.1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
// Game variables
var player;
var bullets = [];
var enemies = [];
var powerUps = [];
var gameStarted = false;
var gameOver = false;
var score = 0;
var level = 1;
var enemiesPerRow = 5;
var enemyRows = 3;
var enemySpawnDelay = 800;
var lastEnemySpawn = 0;
var powerUpChance = 0.2;
var highScore = storage.highScore || 0;
// UI Elements
var scoreTxt = new Text2('SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
var levelTxt = new Text2('LEVEL: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 20;
var instructionsTxt = new Text2('Open mouth to FIRE\nMake noise for SPECIAL ATTACK', {
size: 40,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(instructionsTxt);
instructionsTxt.y = -100;
var livesTxt = new Text2('LIVES: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(livesTxt);
livesTxt.x = 120; // Keep away from top-left corner (menu icon)
livesTxt.y = 20;
// Start the game
function startGame() {
if (!gameStarted) {
gameStarted = true;
// Initialize player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 200;
game.addChild(player);
// Reset game state
score = 0;
level = 1;
bullets = [];
enemies = [];
powerUps = [];
// Start background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
// Start spawning enemies
spawnEnemyWave();
}
}
// Spawn wave of enemies
function spawnEnemyWave() {
var startX = (2048 - enemiesPerRow * 150) / 2 + 75;
var startY = 200;
for (var row = 0; row < enemyRows; row++) {
for (var col = 0; col < enemiesPerRow; col++) {
var enemy = new Enemy();
enemy.x = startX + col * 150;
enemy.y = startY + row * 120;
enemy.health = Math.ceil(level / 3); // Increase health every 3 levels
enemy.speed = 1 + level * 0.5; // Increase speed with level
// Alternate movement direction by row
if (row % 2 === 1) {
enemy.moveDirection = -1;
}
game.addChild(enemy);
enemies.push(enemy);
}
}
}
// Spawn a power-up
function spawnPowerUp(x, y) {
if (Math.random() < powerUpChance) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
game.addChild(powerUp);
powerUps.push(powerUp);
}
}
// Update score display
function updateScoreDisplay() {
scoreTxt.setText("SCORE: " + score);
levelTxt.setText("LEVEL: " + level);
livesTxt.setText("LIVES: " + player.lives);
}
// Check for level completion
function checkLevelComplete() {
if (enemies.length === 0) {
level++;
enemiesPerRow = Math.min(enemiesPerRow + 1, 8);
enemyRows = Math.min(enemyRows + 1, 5);
powerUpChance = Math.min(powerUpChance + 0.05, 0.5);
// Clear all bullets
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
// Spawn new enemy wave
spawnEnemyWave();
// Update level display
updateScoreDisplay();
}
}
// Check for game over conditions
function checkGameOver() {
// Check if player has no lives left
if (player.lives <= 0) {
endGame();
return;
}
// Check if any enemies have reached the bottom
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y > 2732 - 150) {
endGame();
return;
}
}
}
// End game function
function endGame() {
gameOver = true;
gameStarted = false;
// Check for high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Show game over screen
LK.showGameOver();
// Stop music
LK.stopMusic();
}
// Main update function
game.update = function () {
// Start game if face is detected and game not started
if (facekit.mouthCenter && !gameStarted && !gameOver) {
startGame();
}
if (gameStarted && !gameOver) {
// Update player position based on face position
if (facekit.mouthCenter) {
var targetX = facekit.mouthCenter.x;
player.x += (targetX - player.x) * 0.2; // Smooth movement
// Fire weapon if mouth is open
if (facekit.mouthOpen) {
player.fire();
}
// Activate special weapon if making noise
if (facekit.volume > 0.3 && !player.specialWeapon) {
player.activateSpecialWeapon();
}
}
// Update player
player.update();
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Update bullet position
bullet.update();
// Remove bullets that go off screen
if (bullet.y < -100) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check for collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
var destroyed = enemy.hit(bullet.power);
if (destroyed) {
// Add score
score += 10 * level;
// Play explosion sound
LK.getSound('explosion').play();
// Flash effect for destroyed enemy
LK.effects.flashObject(enemy, 0xff0000, 200);
// Possibly spawn power-up
spawnPowerUp(enemy.x, enemy.y);
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
}
// Remove bullet
bullet.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
// Check for collision with player
if (enemies[i].intersects(player)) {
player.lives--;
// Flash screen red
LK.effects.flashScreen(0xff0000, 300);
// Remove enemy
enemies[i].destroy();
enemies.splice(i, 1);
i--;
// Check if game over
if (player.lives <= 0) {
checkGameOver();
} else {
updateScoreDisplay();
}
continue;
}
}
// Update power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
// Update power-up position
powerUp.update();
// Remove power-ups that go off screen
if (powerUp.y > 2732 + 100) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check for collision with player
if (powerUp.intersects(player)) {
// Apply power-up effect
player.activateSpecialWeapon();
// Play power-up sound
LK.getSound('powerup').play();
// Remove power-up
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Check if level is complete
checkLevelComplete();
// Check for game over conditions
checkGameOver();
// Update UI
updateScoreDisplay();
}
};
// Event handlers
game.down = function (x, y, obj) {
// Touch to start game
if (!gameStarted && !gameOver) {
startGame();
}
};
// Start with an instructional message
var startText = new Text2('Face the camera and\nopen your mouth to start!', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2;
game.addChild(startText);
// Remove start text when game starts
var checkStarted = LK.setInterval(function () {
if (gameStarted) {
startText.destroy();
LK.clearInterval(checkStarted);
}
}, 100);