/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
highestLevel: 1
});
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Goal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed at which the goal moves
self.speed = -5;
// Pulsating animation effect
self.pulse = function () {
tween(goalGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(goalGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: self.pulse
});
}
});
};
self.pulse();
self.update = function () {
self.x += self.speed;
// Check if off-screen
if (self.x < -100) {
self.destroy();
return true; // Signal this goal was destroyed
}
return false;
};
return self;
});
var LevelIndicator = Container.expand(function () {
var self = Container.call(this);
var indicatorGraphics = self.attachAsset('levelIndicator', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
var levelText = new Text2('1', {
size: 24,
fill: 0x000000
});
levelText.anchor.set(0.5, 0.5);
self.addChild(levelText);
self.setLevel = function (level) {
levelText.setText(level);
// Flash the indicator when level changes
indicatorGraphics.alpha = 1;
tween(indicatorGraphics, {
alpha: 0.7
}, {
duration: 500,
easing: tween.easeOut
});
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// The player's vertical velocity
self.velocity = 0;
self.gravity = 0.5;
// Base downward movement
self.baseDownForce = 3;
// Multiplier for volume-based ascension
self.liftMultiplier = 15;
// Whether the player is allowed to move (for intro/outro animations)
self.canMove = true;
self.update = function () {
if (!self.canMove) {
return;
}
// Apply volume-based lift force
if (facekit.volume > 0.05) {
self.velocity = -facekit.volume * self.liftMultiplier;
// Visualize volume with a tint (blue to red)
var intensity = Math.min(facekit.volume * 2, 1);
var color = tween.linear(intensity, 0x3498db, 0xe74c3c);
playerGraphics.tint = color;
} else {
// Reset tint when quiet
playerGraphics.tint = 0x3498db;
// Apply gravity
self.velocity += self.gravity;
}
// Apply constant downward force
self.velocity += self.baseDownForce;
// Limit max velocity
self.velocity = Math.max(Math.min(self.velocity, 25), -25);
// Update position
self.y += self.velocity;
// Constrain to screen
if (self.y < 80) {
self.y = 80;
self.velocity = 0;
}
if (self.y > 2732 - 80) {
self.y = 2732 - 80;
self.velocity = 0;
}
};
// Reset player state
self.reset = function (startX, startY) {
self.x = startX;
self.y = startY;
self.velocity = 0;
self.canMove = true;
playerGraphics.alpha = 1;
playerGraphics.tint = 0x3498db;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed at which the wall moves
self.speed = -5;
self.update = function () {
self.x += self.speed;
// Remove if off-screen
if (self.x < -100) {
self.destroy();
return true; // Signal this wall was destroyed
}
return false;
};
// Method to change the wall's appearance
self.setProperties = function (width, height, color) {
wallGraphics.width = width;
wallGraphics.height = height;
wallGraphics.tint = color;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game state variables
var currentLevel = storage.currentLevel || 1;
var highestLevel = storage.highestLevel || 1;
var walls = [];
var player;
var goal;
var levelIndicator;
var gameActive = false;
var levelCompleted = false;
var volumeIndicator;
var totalLevels = 30;
var volumeHistory = [];
// Level generation parameters
var obstaclePatterns = [
// Level 1-5: Simple patterns
function () {
return [{
x: 2048,
y: 1400,
width: 500,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 800,
width: 500,
height: 30
}, {
x: 2048,
y: 1800,
width: 500,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 1366,
width: 700,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 600,
width: 400,
height: 30
}, {
x: 2048,
y: 2100,
width: 400,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 900,
width: 300,
height: 30
}, {
x: 2048,
y: 1800,
width: 300,
height: 30
}];
},
// Level 6-10: Moving gaps
function () {
return [{
x: 2048,
y: 700,
width: 800,
height: 30
}, {
x: 2048,
y: 1900,
width: 800,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 500,
width: 600,
height: 30
}, {
x: 2048,
y: 1200,
width: 600,
height: 30
}, {
x: 2048,
y: 1900,
width: 600,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 800,
width: 400,
height: 30
}, {
x: 2048,
y: 1500,
width: 400,
height: 30
}, {
x: 2048,
y: 2200,
width: 400,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 600,
width: 350,
height: 30
}, {
x: 2048,
y: 1366,
width: 900,
height: 30
}, {
x: 2048,
y: 2100,
width: 350,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 400,
width: 300,
height: 30
}, {
x: 2048,
y: 900,
width: 300,
height: 30
}, {
x: 2048,
y: 1400,
width: 300,
height: 30
}, {
x: 2048,
y: 1900,
width: 300,
height: 30
}, {
x: 2048,
y: 2400,
width: 300,
height: 30
}];
},
// Level 11-15: Complex patterns
function () {
var obstacles = [];
for (var i = 0; i < 8; i++) {
obstacles.push({
x: 2048 + i * 250,
y: 700 + i % 2 * 1200,
width: 150,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 6; i++) {
obstacles.push({
x: 2048 + i * 300,
y: 600 + i % 3 * 700,
width: 200,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 5; i++) {
obstacles.push({
x: 2048 + i * 350,
y: 700 + Math.sin(i * 0.7) * 900,
width: 250,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
var baseY = 1366;
for (var i = 0; i < 7; i++) {
var offset = i % 2 === 0 ? 700 : -700;
obstacles.push({
x: 2048 + i * 300,
y: baseY + offset,
width: 180,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 10; i++) {
obstacles.push({
x: 2048 + i * 250,
y: 800 + i % 4 * 400,
width: 150,
height: 30
});
}
return obstacles;
},
// Level 16-20: Increasing difficulty
function () {
var obstacles = [];
var positions = [500, 900, 1300, 1700, 2100];
for (var i = 0; i < 8; i++) {
var yPos = positions[Math.floor(Math.random() * positions.length)];
obstacles.push({
x: 2048 + i * 300,
y: yPos,
width: 180,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 6; i++) {
obstacles.push({
x: 2048 + i * 400,
y: 700 + Math.cos(i * 0.9) * 900,
width: 300,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 15; i++) {
if (i % 3 !== 0) {
// Skip every third to create gaps
obstacles.push({
x: 2048 + i * 200,
y: 500 + i % 5 * 400,
width: 150,
height: 30
});
}
}
return obstacles;
}, function () {
var obstacles = [];
var zigzag = true;
for (var i = 0; i < 12; i++) {
var yPos = zigzag ? 600 + i % 3 * 700 : 2100 - i % 3 * 700;
obstacles.push({
x: 2048 + i * 230,
y: yPos,
width: 150,
height: 30
});
if (i % 3 === 2) {
zigzag = !zigzag;
}
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 20; i++) {
// Create a spiral pattern
var angle = i * 0.3;
var radius = 500 + i * 30;
var yPos = 1366 + Math.sin(angle) * (radius / 3);
obstacles.push({
x: 2048 + i * 200,
y: yPos,
width: 120,
height: 30
});
}
return obstacles;
},
// Level 21-25: Advanced patterns
function () {
var obstacles = [];
// Create a wave pattern
for (var i = 0; i < 15; i++) {
obstacles.push({
x: 2048 + i * 180,
y: 1366 + Math.sin(i * 0.5) * 800,
width: 100,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create a double helix pattern
for (var i = 0; i < 20; i++) {
obstacles.push({
x: 2048 + i * 150,
y: 1366 + Math.sin(i * 0.4) * 700,
width: 90,
height: 30
});
obstacles.push({
x: 2048 + i * 150,
y: 1366 + Math.sin(i * 0.4 + Math.PI) * 700,
width: 90,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create random clusters
for (var i = 0; i < 5; i++) {
var clusterX = 2048 + i * 500;
for (var j = 0; j < 4; j++) {
obstacles.push({
x: clusterX + (Math.random() * 200 - 100),
y: 600 + j * 500 + (Math.random() * 200 - 100),
width: 120,
height: 30
});
}
}
return obstacles;
}, function () {
var obstacles = [];
// Create a pulsating pattern
for (var i = 0; i < 15; i++) {
var width = 100 + Math.sin(i * 0.4) * 50;
obstacles.push({
x: 2048 + i * 200,
y: 600 + i % 4 * 500,
width: width,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create a moving wall with small openings
for (var i = 0; i < 30; i++) {
if (i % 5 !== 2) {
// Create openings
obstacles.push({
x: 2048 + Math.floor(i / 5) * 400,
y: 300 + i * 80,
width: 200,
height: 30
});
}
}
return obstacles;
},
// Level 26-30: Expert challenges
function () {
var obstacles = [];
// Create a tight slalom
for (var i = 0; i < 12; i++) {
var yOffset = i % 2 === 0 ? 1000 : -1000;
obstacles.push({
x: 2048 + i * 250,
y: 1366 + yOffset,
width: 1500,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create a tunnel that gets progressively narrower
var tunnelWidth = 1000;
for (var i = 0; i < 10; i++) {
tunnelWidth = Math.max(300, tunnelWidth - 70);
var halfTunnel = tunnelWidth / 2;
obstacles.push({
x: 2048 + i * 300,
y: 1366 - halfTunnel - 30,
width: 200,
height: 30
});
obstacles.push({
x: 2048 + i * 300,
y: 1366 + halfTunnel,
width: 200,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create moving blocks that require precise timing
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 5; j++) {
if ((i + j) % 2 === 0) {
obstacles.push({
x: 2048 + i * 350,
y: 400 + j * 500,
width: 150,
height: 30
});
}
}
}
return obstacles;
}, function () {
var obstacles = [];
// Create a complex maze-like pattern
var positions = [[0, 1, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 0, 1, 1, 1], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0]];
for (var i = 0; i < positions.length; i++) {
for (var j = 0; j < positions[i].length; j++) {
if (positions[i][j] === 1) {
obstacles.push({
x: 2048 + i * 300,
y: 500 + j * 400,
width: 220,
height: 30
});
}
}
}
return obstacles;
}, function () {
var obstacles = [];
// The final challenge - all techniques combined
// Wave pattern
for (var i = 0; i < 5; i++) {
obstacles.push({
x: 2048 + i * 300,
y: 1366 + Math.sin(i * 0.8) * 600,
width: 180,
height: 30
});
}
// Tight slalom
for (var i = 0; i < 3; i++) {
var yOffset = i % 2 === 0 ? 800 : -800;
obstacles.push({
x: 2048 + 1500 + i * 250,
y: 1366 + yOffset,
width: 1200,
height: 30
});
}
// Final gauntlet
for (var i = 0; i < 5; i++) {
if (i !== 2) {
// Gap in the middle
obstacles.push({
x: 2048 + 2500,
y: 400 + i * 500,
width: 100,
height: 30
});
}
}
return obstacles;
}];
// Initialize UI elements
var scoreTxt = new Text2('Level: 1/' + totalLevels, {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var instructionsTxt = new Text2('Make noise to rise\nStay quiet to fall', {
size: 60,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
instructionsTxt.y = 100;
LK.gui.top.addChild(instructionsTxt);
// Volume indicator
volumeIndicator = new Container();
var volumeBarBg = LK.getAsset('wall', {
anchorX: 0,
anchorY: 0.5,
width: 50,
height: 400,
tint: 0x000000,
alpha: 0.3
});
volumeIndicator.addChild(volumeBarBg);
var volumeBar = LK.getAsset('wall', {
anchorX: 0,
anchorY: 1,
width: 50,
height: 0,
tint: 0x3498db
});
volumeIndicator.addChild(volumeBar);
volumeIndicator.x = 150;
volumeIndicator.y = 1366; // Center of screen height
LK.gui.addChild(volumeIndicator);
// Level indicator
levelIndicator = new LevelIndicator();
levelIndicator.x = 120;
levelIndicator.y = 120;
LK.gui.addChild(levelIndicator);
levelIndicator.setLevel(currentLevel);
// Initialize the player
player = new Player();
player.x = 400;
player.y = 1366; // Center of screen height
game.addChild(player);
// Set up the first level
setupLevel(currentLevel);
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Main update function
game.update = function () {
// Update volume history for smoother response
volumeHistory.push(facekit.volume);
if (volumeHistory.length > 5) {
volumeHistory.shift();
}
// Calculate average volume
var avgVolume = 0;
for (var i = 0; i < volumeHistory.length; i++) {
avgVolume += volumeHistory[i];
}
avgVolume /= volumeHistory.length;
// Update volume indicator
volumeBar.height = avgVolume * 400;
volumeBar.tint = avgVolume < 0.3 ? 0x3498db : avgVolume < 0.7 ? 0xf39c12 : 0xe74c3c;
// Only update gameplay if the game is active
if (gameActive && !levelCompleted) {
// Update all walls
for (var i = walls.length - 1; i >= 0; i--) {
var removed = walls[i].update();
if (removed) {
walls.splice(i, 1);
}
}
// Check if goal is reached or destroyed
if (goal) {
var goalRemoved = goal.update();
if (goalRemoved) {
goal = null;
} else if (player.intersects(goal)) {
levelCompleted = true;
completeLevel();
}
}
// Check collisions with walls
var collision = false;
for (var i = 0; i < walls.length; i++) {
if (player.intersects(walls[i])) {
collision = true;
break;
}
}
if (collision) {
gameOver();
}
// Calculate spawn timing based on level
if (LK.ticks % Math.max(120 - currentLevel * 2, 30) === 0 && walls.length < 40) {
spawnObstacles();
}
// Spawn goal after certain time
if (LK.ticks % 600 === 0 && !goal) {
spawnGoal();
}
}
// Hide instructions after 3 seconds
if (LK.ticks === 180) {
tween(instructionsTxt, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}
};
// Setup a new level
function setupLevel(level) {
// Ensure level is in bounds
level = Math.max(1, Math.min(level, totalLevels));
currentLevel = level;
// Update storage
storage.currentLevel = currentLevel;
if (currentLevel > highestLevel) {
highestLevel = currentLevel;
storage.highestLevel = highestLevel;
}
// Update score text
scoreTxt.setText('Level: ' + currentLevel + '/' + totalLevels);
// Update level indicator
levelIndicator.setLevel(currentLevel);
// Reset game state
walls = [];
if (goal) {
goal.destroy();
goal = null;
}
levelCompleted = false;
// Reset player position
player.reset(400, 1366);
// Set wall speed based on level
Wall.prototype.speed = -5 - currentLevel * 0.2;
if (goal) {
goal.speed = Wall.prototype.speed;
}
// Start game after a short delay
LK.setTimeout(function () {
gameActive = true;
}, 1000);
}
// Spawn obstacles based on the current level
function spawnObstacles() {
// Get the appropriate pattern generator for the current level
var patternIndex = Math.min(currentLevel - 1, obstaclePatterns.length - 1);
var obstacles = obstaclePatterns[patternIndex]();
// Create wall objects
for (var i = 0; i < obstacles.length; i++) {
var wall = new Wall();
wall.x = obstacles[i].x;
wall.y = obstacles[i].y;
wall.setProperties(obstacles[i].width, obstacles[i].height, 0xe74c3c);
walls.push(wall);
game.addChild(wall);
}
}
// Spawn the level goal
function spawnGoal() {
goal = new Goal();
goal.x = 2048 + 200; // Start off screen
goal.y = 1366; // Center of screen height
goal.speed = Wall.prototype.speed; // Match wall speed
game.addChild(goal);
}
// Handle level completion
function completeLevel() {
gameActive = false;
// Play sound
LK.getSound('levelComplete').play();
// Animate player
tween(player, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeIn
});
}
});
// Show level complete message
var levelCompleteTxt = new Text2('Level ' + currentLevel + ' Complete!', {
size: 120,
fill: 0xFFFFFF
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 1024;
levelCompleteTxt.y = 1366;
levelCompleteTxt.alpha = 0;
LK.gui.addChild(levelCompleteTxt);
tween(levelCompleteTxt, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(levelCompleteTxt, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
levelCompleteTxt.destroy();
// Check if the player has completed all levels
if (currentLevel === totalLevels) {
// Game completed!
var gameCompleteTxt = new Text2('Congratulations!\nYou completed all levels!', {
size: 100,
fill: 0xFFFFFF
});
gameCompleteTxt.anchor.set(0.5, 0.5);
gameCompleteTxt.x = 1024;
gameCompleteTxt.y = 1366;
LK.gui.addChild(gameCompleteTxt);
LK.setTimeout(function () {
gameCompleteTxt.destroy();
currentLevel = 1;
setupLevel(currentLevel);
}, 3000);
} else {
// Next level
setupLevel(currentLevel + 1);
}
}
});
}
});
}
// Handle game over
function gameOver() {
gameActive = false;
// Play sound
LK.getSound('gameOver').play();
// Animate player
player.canMove = false;
tween(player, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeOut
});
// Show game over message
var gameOverTxt = new Text2('Level Failed', {
size: 120,
fill: 0xFFFFFF
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 1024;
gameOverTxt.y = 1366;
gameOverTxt.alpha = 0;
LK.gui.addChild(gameOverTxt);
tween(gameOverTxt, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(gameOverTxt, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
gameOverTxt.destroy();
setupLevel(currentLevel);
}
});
}, 1500);
}
});
// Screen flash
LK.effects.flashScreen(0xe74c3c, 500);
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
highestLevel: 1
});
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Goal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed at which the goal moves
self.speed = -5;
// Pulsating animation effect
self.pulse = function () {
tween(goalGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(goalGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: self.pulse
});
}
});
};
self.pulse();
self.update = function () {
self.x += self.speed;
// Check if off-screen
if (self.x < -100) {
self.destroy();
return true; // Signal this goal was destroyed
}
return false;
};
return self;
});
var LevelIndicator = Container.expand(function () {
var self = Container.call(this);
var indicatorGraphics = self.attachAsset('levelIndicator', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
var levelText = new Text2('1', {
size: 24,
fill: 0x000000
});
levelText.anchor.set(0.5, 0.5);
self.addChild(levelText);
self.setLevel = function (level) {
levelText.setText(level);
// Flash the indicator when level changes
indicatorGraphics.alpha = 1;
tween(indicatorGraphics, {
alpha: 0.7
}, {
duration: 500,
easing: tween.easeOut
});
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// The player's vertical velocity
self.velocity = 0;
self.gravity = 0.5;
// Base downward movement
self.baseDownForce = 3;
// Multiplier for volume-based ascension
self.liftMultiplier = 15;
// Whether the player is allowed to move (for intro/outro animations)
self.canMove = true;
self.update = function () {
if (!self.canMove) {
return;
}
// Apply volume-based lift force
if (facekit.volume > 0.05) {
self.velocity = -facekit.volume * self.liftMultiplier;
// Visualize volume with a tint (blue to red)
var intensity = Math.min(facekit.volume * 2, 1);
var color = tween.linear(intensity, 0x3498db, 0xe74c3c);
playerGraphics.tint = color;
} else {
// Reset tint when quiet
playerGraphics.tint = 0x3498db;
// Apply gravity
self.velocity += self.gravity;
}
// Apply constant downward force
self.velocity += self.baseDownForce;
// Limit max velocity
self.velocity = Math.max(Math.min(self.velocity, 25), -25);
// Update position
self.y += self.velocity;
// Constrain to screen
if (self.y < 80) {
self.y = 80;
self.velocity = 0;
}
if (self.y > 2732 - 80) {
self.y = 2732 - 80;
self.velocity = 0;
}
};
// Reset player state
self.reset = function (startX, startY) {
self.x = startX;
self.y = startY;
self.velocity = 0;
self.canMove = true;
playerGraphics.alpha = 1;
playerGraphics.tint = 0x3498db;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed at which the wall moves
self.speed = -5;
self.update = function () {
self.x += self.speed;
// Remove if off-screen
if (self.x < -100) {
self.destroy();
return true; // Signal this wall was destroyed
}
return false;
};
// Method to change the wall's appearance
self.setProperties = function (width, height, color) {
wallGraphics.width = width;
wallGraphics.height = height;
wallGraphics.tint = color;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game state variables
var currentLevel = storage.currentLevel || 1;
var highestLevel = storage.highestLevel || 1;
var walls = [];
var player;
var goal;
var levelIndicator;
var gameActive = false;
var levelCompleted = false;
var volumeIndicator;
var totalLevels = 30;
var volumeHistory = [];
// Level generation parameters
var obstaclePatterns = [
// Level 1-5: Simple patterns
function () {
return [{
x: 2048,
y: 1400,
width: 500,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 800,
width: 500,
height: 30
}, {
x: 2048,
y: 1800,
width: 500,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 1366,
width: 700,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 600,
width: 400,
height: 30
}, {
x: 2048,
y: 2100,
width: 400,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 900,
width: 300,
height: 30
}, {
x: 2048,
y: 1800,
width: 300,
height: 30
}];
},
// Level 6-10: Moving gaps
function () {
return [{
x: 2048,
y: 700,
width: 800,
height: 30
}, {
x: 2048,
y: 1900,
width: 800,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 500,
width: 600,
height: 30
}, {
x: 2048,
y: 1200,
width: 600,
height: 30
}, {
x: 2048,
y: 1900,
width: 600,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 800,
width: 400,
height: 30
}, {
x: 2048,
y: 1500,
width: 400,
height: 30
}, {
x: 2048,
y: 2200,
width: 400,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 600,
width: 350,
height: 30
}, {
x: 2048,
y: 1366,
width: 900,
height: 30
}, {
x: 2048,
y: 2100,
width: 350,
height: 30
}];
}, function () {
return [{
x: 2048,
y: 400,
width: 300,
height: 30
}, {
x: 2048,
y: 900,
width: 300,
height: 30
}, {
x: 2048,
y: 1400,
width: 300,
height: 30
}, {
x: 2048,
y: 1900,
width: 300,
height: 30
}, {
x: 2048,
y: 2400,
width: 300,
height: 30
}];
},
// Level 11-15: Complex patterns
function () {
var obstacles = [];
for (var i = 0; i < 8; i++) {
obstacles.push({
x: 2048 + i * 250,
y: 700 + i % 2 * 1200,
width: 150,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 6; i++) {
obstacles.push({
x: 2048 + i * 300,
y: 600 + i % 3 * 700,
width: 200,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 5; i++) {
obstacles.push({
x: 2048 + i * 350,
y: 700 + Math.sin(i * 0.7) * 900,
width: 250,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
var baseY = 1366;
for (var i = 0; i < 7; i++) {
var offset = i % 2 === 0 ? 700 : -700;
obstacles.push({
x: 2048 + i * 300,
y: baseY + offset,
width: 180,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 10; i++) {
obstacles.push({
x: 2048 + i * 250,
y: 800 + i % 4 * 400,
width: 150,
height: 30
});
}
return obstacles;
},
// Level 16-20: Increasing difficulty
function () {
var obstacles = [];
var positions = [500, 900, 1300, 1700, 2100];
for (var i = 0; i < 8; i++) {
var yPos = positions[Math.floor(Math.random() * positions.length)];
obstacles.push({
x: 2048 + i * 300,
y: yPos,
width: 180,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 6; i++) {
obstacles.push({
x: 2048 + i * 400,
y: 700 + Math.cos(i * 0.9) * 900,
width: 300,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 15; i++) {
if (i % 3 !== 0) {
// Skip every third to create gaps
obstacles.push({
x: 2048 + i * 200,
y: 500 + i % 5 * 400,
width: 150,
height: 30
});
}
}
return obstacles;
}, function () {
var obstacles = [];
var zigzag = true;
for (var i = 0; i < 12; i++) {
var yPos = zigzag ? 600 + i % 3 * 700 : 2100 - i % 3 * 700;
obstacles.push({
x: 2048 + i * 230,
y: yPos,
width: 150,
height: 30
});
if (i % 3 === 2) {
zigzag = !zigzag;
}
}
return obstacles;
}, function () {
var obstacles = [];
for (var i = 0; i < 20; i++) {
// Create a spiral pattern
var angle = i * 0.3;
var radius = 500 + i * 30;
var yPos = 1366 + Math.sin(angle) * (radius / 3);
obstacles.push({
x: 2048 + i * 200,
y: yPos,
width: 120,
height: 30
});
}
return obstacles;
},
// Level 21-25: Advanced patterns
function () {
var obstacles = [];
// Create a wave pattern
for (var i = 0; i < 15; i++) {
obstacles.push({
x: 2048 + i * 180,
y: 1366 + Math.sin(i * 0.5) * 800,
width: 100,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create a double helix pattern
for (var i = 0; i < 20; i++) {
obstacles.push({
x: 2048 + i * 150,
y: 1366 + Math.sin(i * 0.4) * 700,
width: 90,
height: 30
});
obstacles.push({
x: 2048 + i * 150,
y: 1366 + Math.sin(i * 0.4 + Math.PI) * 700,
width: 90,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create random clusters
for (var i = 0; i < 5; i++) {
var clusterX = 2048 + i * 500;
for (var j = 0; j < 4; j++) {
obstacles.push({
x: clusterX + (Math.random() * 200 - 100),
y: 600 + j * 500 + (Math.random() * 200 - 100),
width: 120,
height: 30
});
}
}
return obstacles;
}, function () {
var obstacles = [];
// Create a pulsating pattern
for (var i = 0; i < 15; i++) {
var width = 100 + Math.sin(i * 0.4) * 50;
obstacles.push({
x: 2048 + i * 200,
y: 600 + i % 4 * 500,
width: width,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create a moving wall with small openings
for (var i = 0; i < 30; i++) {
if (i % 5 !== 2) {
// Create openings
obstacles.push({
x: 2048 + Math.floor(i / 5) * 400,
y: 300 + i * 80,
width: 200,
height: 30
});
}
}
return obstacles;
},
// Level 26-30: Expert challenges
function () {
var obstacles = [];
// Create a tight slalom
for (var i = 0; i < 12; i++) {
var yOffset = i % 2 === 0 ? 1000 : -1000;
obstacles.push({
x: 2048 + i * 250,
y: 1366 + yOffset,
width: 1500,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create a tunnel that gets progressively narrower
var tunnelWidth = 1000;
for (var i = 0; i < 10; i++) {
tunnelWidth = Math.max(300, tunnelWidth - 70);
var halfTunnel = tunnelWidth / 2;
obstacles.push({
x: 2048 + i * 300,
y: 1366 - halfTunnel - 30,
width: 200,
height: 30
});
obstacles.push({
x: 2048 + i * 300,
y: 1366 + halfTunnel,
width: 200,
height: 30
});
}
return obstacles;
}, function () {
var obstacles = [];
// Create moving blocks that require precise timing
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 5; j++) {
if ((i + j) % 2 === 0) {
obstacles.push({
x: 2048 + i * 350,
y: 400 + j * 500,
width: 150,
height: 30
});
}
}
}
return obstacles;
}, function () {
var obstacles = [];
// Create a complex maze-like pattern
var positions = [[0, 1, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 0, 1, 1, 1], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0]];
for (var i = 0; i < positions.length; i++) {
for (var j = 0; j < positions[i].length; j++) {
if (positions[i][j] === 1) {
obstacles.push({
x: 2048 + i * 300,
y: 500 + j * 400,
width: 220,
height: 30
});
}
}
}
return obstacles;
}, function () {
var obstacles = [];
// The final challenge - all techniques combined
// Wave pattern
for (var i = 0; i < 5; i++) {
obstacles.push({
x: 2048 + i * 300,
y: 1366 + Math.sin(i * 0.8) * 600,
width: 180,
height: 30
});
}
// Tight slalom
for (var i = 0; i < 3; i++) {
var yOffset = i % 2 === 0 ? 800 : -800;
obstacles.push({
x: 2048 + 1500 + i * 250,
y: 1366 + yOffset,
width: 1200,
height: 30
});
}
// Final gauntlet
for (var i = 0; i < 5; i++) {
if (i !== 2) {
// Gap in the middle
obstacles.push({
x: 2048 + 2500,
y: 400 + i * 500,
width: 100,
height: 30
});
}
}
return obstacles;
}];
// Initialize UI elements
var scoreTxt = new Text2('Level: 1/' + totalLevels, {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var instructionsTxt = new Text2('Make noise to rise\nStay quiet to fall', {
size: 60,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
instructionsTxt.y = 100;
LK.gui.top.addChild(instructionsTxt);
// Volume indicator
volumeIndicator = new Container();
var volumeBarBg = LK.getAsset('wall', {
anchorX: 0,
anchorY: 0.5,
width: 50,
height: 400,
tint: 0x000000,
alpha: 0.3
});
volumeIndicator.addChild(volumeBarBg);
var volumeBar = LK.getAsset('wall', {
anchorX: 0,
anchorY: 1,
width: 50,
height: 0,
tint: 0x3498db
});
volumeIndicator.addChild(volumeBar);
volumeIndicator.x = 150;
volumeIndicator.y = 1366; // Center of screen height
LK.gui.addChild(volumeIndicator);
// Level indicator
levelIndicator = new LevelIndicator();
levelIndicator.x = 120;
levelIndicator.y = 120;
LK.gui.addChild(levelIndicator);
levelIndicator.setLevel(currentLevel);
// Initialize the player
player = new Player();
player.x = 400;
player.y = 1366; // Center of screen height
game.addChild(player);
// Set up the first level
setupLevel(currentLevel);
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Main update function
game.update = function () {
// Update volume history for smoother response
volumeHistory.push(facekit.volume);
if (volumeHistory.length > 5) {
volumeHistory.shift();
}
// Calculate average volume
var avgVolume = 0;
for (var i = 0; i < volumeHistory.length; i++) {
avgVolume += volumeHistory[i];
}
avgVolume /= volumeHistory.length;
// Update volume indicator
volumeBar.height = avgVolume * 400;
volumeBar.tint = avgVolume < 0.3 ? 0x3498db : avgVolume < 0.7 ? 0xf39c12 : 0xe74c3c;
// Only update gameplay if the game is active
if (gameActive && !levelCompleted) {
// Update all walls
for (var i = walls.length - 1; i >= 0; i--) {
var removed = walls[i].update();
if (removed) {
walls.splice(i, 1);
}
}
// Check if goal is reached or destroyed
if (goal) {
var goalRemoved = goal.update();
if (goalRemoved) {
goal = null;
} else if (player.intersects(goal)) {
levelCompleted = true;
completeLevel();
}
}
// Check collisions with walls
var collision = false;
for (var i = 0; i < walls.length; i++) {
if (player.intersects(walls[i])) {
collision = true;
break;
}
}
if (collision) {
gameOver();
}
// Calculate spawn timing based on level
if (LK.ticks % Math.max(120 - currentLevel * 2, 30) === 0 && walls.length < 40) {
spawnObstacles();
}
// Spawn goal after certain time
if (LK.ticks % 600 === 0 && !goal) {
spawnGoal();
}
}
// Hide instructions after 3 seconds
if (LK.ticks === 180) {
tween(instructionsTxt, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}
};
// Setup a new level
function setupLevel(level) {
// Ensure level is in bounds
level = Math.max(1, Math.min(level, totalLevels));
currentLevel = level;
// Update storage
storage.currentLevel = currentLevel;
if (currentLevel > highestLevel) {
highestLevel = currentLevel;
storage.highestLevel = highestLevel;
}
// Update score text
scoreTxt.setText('Level: ' + currentLevel + '/' + totalLevels);
// Update level indicator
levelIndicator.setLevel(currentLevel);
// Reset game state
walls = [];
if (goal) {
goal.destroy();
goal = null;
}
levelCompleted = false;
// Reset player position
player.reset(400, 1366);
// Set wall speed based on level
Wall.prototype.speed = -5 - currentLevel * 0.2;
if (goal) {
goal.speed = Wall.prototype.speed;
}
// Start game after a short delay
LK.setTimeout(function () {
gameActive = true;
}, 1000);
}
// Spawn obstacles based on the current level
function spawnObstacles() {
// Get the appropriate pattern generator for the current level
var patternIndex = Math.min(currentLevel - 1, obstaclePatterns.length - 1);
var obstacles = obstaclePatterns[patternIndex]();
// Create wall objects
for (var i = 0; i < obstacles.length; i++) {
var wall = new Wall();
wall.x = obstacles[i].x;
wall.y = obstacles[i].y;
wall.setProperties(obstacles[i].width, obstacles[i].height, 0xe74c3c);
walls.push(wall);
game.addChild(wall);
}
}
// Spawn the level goal
function spawnGoal() {
goal = new Goal();
goal.x = 2048 + 200; // Start off screen
goal.y = 1366; // Center of screen height
goal.speed = Wall.prototype.speed; // Match wall speed
game.addChild(goal);
}
// Handle level completion
function completeLevel() {
gameActive = false;
// Play sound
LK.getSound('levelComplete').play();
// Animate player
tween(player, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeIn
});
}
});
// Show level complete message
var levelCompleteTxt = new Text2('Level ' + currentLevel + ' Complete!', {
size: 120,
fill: 0xFFFFFF
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 1024;
levelCompleteTxt.y = 1366;
levelCompleteTxt.alpha = 0;
LK.gui.addChild(levelCompleteTxt);
tween(levelCompleteTxt, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(levelCompleteTxt, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
levelCompleteTxt.destroy();
// Check if the player has completed all levels
if (currentLevel === totalLevels) {
// Game completed!
var gameCompleteTxt = new Text2('Congratulations!\nYou completed all levels!', {
size: 100,
fill: 0xFFFFFF
});
gameCompleteTxt.anchor.set(0.5, 0.5);
gameCompleteTxt.x = 1024;
gameCompleteTxt.y = 1366;
LK.gui.addChild(gameCompleteTxt);
LK.setTimeout(function () {
gameCompleteTxt.destroy();
currentLevel = 1;
setupLevel(currentLevel);
}, 3000);
} else {
// Next level
setupLevel(currentLevel + 1);
}
}
});
}
});
}
// Handle game over
function gameOver() {
gameActive = false;
// Play sound
LK.getSound('gameOver').play();
// Animate player
player.canMove = false;
tween(player, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeOut
});
// Show game over message
var gameOverTxt = new Text2('Level Failed', {
size: 120,
fill: 0xFFFFFF
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 1024;
gameOverTxt.y = 1366;
gameOverTxt.alpha = 0;
LK.gui.addChild(gameOverTxt);
tween(gameOverTxt, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(gameOverTxt, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
gameOverTxt.destroy();
setupLevel(currentLevel);
}
});
}, 1500);
}
});
// Screen flash
LK.effects.flashScreen(0xe74c3c, 500);
}