/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Background = Container.expand(function () {
var self = Container.call(this);
// Create sky gradient
var skyGradient = new Container();
// Create several bands of sky color
var skyColors = [0x1a3c80, 0x3a5c9f, 0x5a7cbf, 0x7a9cdf, 0x9abcff];
var bandHeight = 2732 / skyColors.length;
for (var i = 0; i < skyColors.length; i++) {
var skyBand = LK.getAsset('skyBand' + i, {
width: 2048,
height: bandHeight,
color: skyColors[i],
shape: 'box',
y: i * bandHeight
});
skyGradient.addChild(skyBand);
}
self.addChild(skyGradient);
// Create buildings
var buildings = new Container();
self.addChild(buildings);
// Far buildings (darker)
var buildingColors = [0x222a40, 0x323a50, 0x424a60];
var buildingCount = 15;
for (var i = 0; i < buildingCount; i++) {
var buildingWidth = 100 + Math.random() * 150;
var buildingHeight = 300 + Math.random() * 400;
var building = LK.getAsset('building_far_' + i, {
width: buildingWidth,
height: buildingHeight,
color: buildingColors[Math.floor(Math.random() * buildingColors.length)],
shape: 'box',
x: i * (2048 / buildingCount),
y: 2732 - buildingHeight,
anchorY: 0
});
buildings.addChild(building);
}
// Near buildings (slightly lighter)
var nearBuildingColors = [0x303a50, 0x404a60, 0x505a70];
var nearBuildingCount = 10;
for (var i = 0; i < nearBuildingCount; i++) {
var buildingWidth = 180 + Math.random() * 150;
var buildingHeight = 500 + Math.random() * 600;
var building = LK.getAsset('building_near_' + i, {
width: buildingWidth,
height: buildingHeight,
color: nearBuildingColors[Math.floor(Math.random() * nearBuildingColors.length)],
shape: 'box',
x: i * (2048 / nearBuildingCount),
y: 2732 - buildingHeight,
anchorY: 0
});
buildings.addChild(building);
}
// Add some windows to buildings (as small yellow rectangles)
for (var b = 0; b < buildings.children.length; b++) {
var building = buildings.children[b];
var windowCount = Math.floor(building.width / 30) * Math.floor(building.height / 40);
// Limit window count for performance
windowCount = Math.min(windowCount, 20);
for (var w = 0; w < windowCount; w++) {
var windowSize = 10 + Math.random() * 10;
var windowX = Math.random() * (building.width - windowSize);
var windowY = Math.random() * (building.height - windowSize);
// Only add some windows (randomized lighting effect)
if (Math.random() > 0.4) {
var windowLight = LK.getAsset('window_' + b + '_' + w, {
width: windowSize,
height: windowSize,
color: Math.random() > 0.7 ? 0xffcc66 : 0x888888,
// Some windows lit, some not
shape: 'box',
x: building.x + windowX,
y: building.y + windowY
});
self.addChild(windowLight);
}
}
}
// Add clouds
var clouds = new Container();
self.clouds = clouds;
self.addChild(clouds);
var cloudCount = 6;
for (var i = 0; i < cloudCount; i++) {
var cloudWidth = 200 + Math.random() * 300;
var cloudHeight = 100 + Math.random() * 100;
var cloud = LK.getAsset('cloud_' + i, {
width: cloudWidth,
height: cloudHeight,
color: 0xddddff,
shape: 'ellipse',
x: Math.random() * 2048,
y: 200 + Math.random() * 400,
alpha: 0.7
});
// Add some variation to clouds with slight scaling
cloud.scaleX = 0.8 + Math.random() * 0.4;
cloud.scaleY = 0.8 + Math.random() * 0.4;
// Store cloud speed for animation
cloud.speedX = 0.2 + Math.random() * 0.3;
clouds.addChild(cloud);
}
// Update function to animate clouds
self.update = function () {
// Move clouds horizontally
for (var i = 0; i < clouds.children.length; i++) {
var cloud = clouds.children[i];
cloud.x += cloud.speedX;
// Loop clouds when they move off screen
if (cloud.x > 2048 + cloud.width) {
cloud.x = -cloud.width;
cloud.y = 200 + Math.random() * 400;
}
}
};
return self;
});
var BreathingCircle = Container.expand(function () {
var self = Container.call(this);
// Create the breathing circle
var innerCircle = self.attachAsset('centerCircle', {
width: 200,
height: 200,
color: 0x3a5c9f,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4
});
var outerCircle = LK.getAsset('centerCircle', {
width: 240,
height: 240,
color: 0x5a7cbf,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2
});
self.addChild(outerCircle);
// Breathing animation state
self.breathPhase = 0; // 0: inhale growing, 1: hold, 2: exhale shrinking, 3: hold
self.breathTimer = 0;
self.inhaleDuration = 240; // 4 seconds
self.holdDuration = 120; // 2 seconds
self.exhaleDuration = 240; // 4 seconds
self.restDuration = 60; // 1 second
// Text guide for breathing
self.breathText = new Text2("Breathe In", {
size: 40,
fill: 0xffffff
});
self.breathText.anchor.set(0.5, 0.5);
self.breathText.y = -160;
self.addChild(self.breathText);
// Update method to animate the breathing circle
self.update = function () {
self.breathTimer++;
// Determine current phase based on timer
if (self.breathPhase === 0) {
// Inhale
var progress = self.breathTimer / self.inhaleDuration;
var scale = 1 + progress * 0.5;
innerCircle.scale.x = innerCircle.scale.y = scale;
outerCircle.scale.x = outerCircle.scale.y = 0.9 + progress * 0.5;
self.breathText.setText("Breathe In");
if (self.breathTimer >= self.inhaleDuration) {
self.breathPhase = 1;
self.breathTimer = 0;
}
} else if (self.breathPhase === 1) {
// Hold after inhale
self.breathText.setText("Hold");
if (self.breathTimer >= self.holdDuration) {
self.breathPhase = 2;
self.breathTimer = 0;
}
} else if (self.breathPhase === 2) {
// Exhale
var progress = self.breathTimer / self.exhaleDuration;
var scale = 1.5 - progress * 0.5;
innerCircle.scale.x = innerCircle.scale.y = scale;
outerCircle.scale.x = outerCircle.scale.y = 1.4 - progress * 0.5;
self.breathText.setText("Breathe Out");
if (self.breathTimer >= self.exhaleDuration) {
self.breathPhase = 3;
self.breathTimer = 0;
}
} else {
// Hold after exhale
self.breathText.setText("Rest");
if (self.breathTimer >= self.restDuration) {
self.breathPhase = 0;
self.breathTimer = 0;
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.direction = {
x: 0,
y: -1
}; // Default upward
self.lifespan = 120; // 2 seconds at 60fps
self.age = 0;
self.damage = 1;
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
self.age++;
if (self.age > self.lifespan) {
self.markForDeletion = true;
}
};
return self;
});
var Criminal = Container.expand(function () {
var self = Container.call(this);
var criminalGraphics = self.attachAsset('criminal', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 2; // Reduced from 3
self.shootCooldown = 0;
self.shootDelay = 120; // Frames between shots
self.attackRange = 300; // Reduced from 500
self.movePattern = Math.floor(Math.random() * 3); // 0: direct, 1: zigzag, 2: circle
self.moveTimer = 0;
self.update = function () {
// Target the player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Movement based on pattern
if (distance > 100) {
// Keep some distance
var moveX = 0;
var moveY = 0;
switch (self.movePattern) {
case 0:
// Direct
moveX = dx / distance;
moveY = dy / distance;
break;
case 1:
// Zigzag
self.moveTimer++;
moveX = dx / distance + Math.sin(self.moveTimer * 0.1) * 0.5;
moveY = dy / distance;
break;
case 2:
// Circle
self.moveTimer++;
moveX = dx / distance + Math.sin(self.moveTimer * 0.05) * 0.8;
moveY = dy / distance + Math.cos(self.moveTimer * 0.05) * 0.8;
break;
}
self.x += moveX * self.speed;
self.y += moveY * self.speed;
}
// Shooting logic
if (distance < self.attackRange) {
if (self.shootCooldown <= 0) {
// Create enemy bullet
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction toward player
var dirX = dx / distance;
var dirY = dy / distance;
bullet.direction = {
x: dirX,
y: dirY
};
// Mark as enemy bullet
bullet.isEnemyBullet = true;
// Add to game's bullets array
bullets.push(bullet);
game.addChild(bullet);
// Reset cooldown with some randomness
self.shootCooldown = self.shootDelay + Math.random() * 60;
}
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function () {
self.health--;
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
LK.getSound('criminal_down').play();
LK.setScore(LK.getScore() + 1);
self.markForDeletion = true;
// Track criminal elimination task
var eliminateTask = tasks.find(function (task) {
return task.id === 'eliminate';
});
if (eliminateTask && !eliminateTask.completed) {
eliminateTask.count++;
if (eliminateTask.count >= eliminateTask.target) {
eliminateTask.completed = true;
LK.effects.flashObject(taskTexts[1], 0x00FF00, 1000);
}
updateTaskDisplay();
}
}
};
return self;
});
var EmergencyCall = Container.expand(function () {
var self = Container.call(this);
var callGraphics = self.attachAsset('emergencyCall', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 600; // 10 seconds at 60fps
self.age = 0;
self.flashInterval = 30;
self.update = function () {
self.age++;
// Flash effect
if (self.age % self.flashInterval === 0) {
if (callGraphics.alpha === 1) {
tween(callGraphics, {
alpha: 0.3
}, {
duration: 500
});
} else {
tween(callGraphics, {
alpha: 1
}, {
duration: 500
});
}
}
// Expire after lifetime
if (self.age > self.lifetime) {
self.markForDeletion = true;
}
};
self.down = function () {
// Responding to call
LK.getSound('radiochatter').play();
// Spawn criminals from top of screen, using emergency call's X position
spawnCriminals(self.x, 0, 2 + Math.floor(Math.random() * 3));
self.markForDeletion = true;
// Increase score for responding
LK.setScore(LK.getScore() + 2);
// Track emergency call response task
var respondTask = tasks.find(function (task) {
return task.id === 'respond';
});
if (respondTask && !respondTask.completed) {
respondTask.count++;
if (respondTask.count >= respondTask.target) {
respondTask.completed = true;
LK.effects.flashObject(taskTexts[0], 0x00FF00, 1000);
}
updateTaskDisplay();
}
};
return self;
});
var MeditationGuide = Container.expand(function () {
var self = Container.call(this);
// Create a text display for meditation prompts
self.promptText = new Text2("Breathe deeply...", {
size: 60,
fill: 0xffffff,
align: 'center'
});
self.promptText.anchor.set(0.5, 0.5);
self.addChild(self.promptText);
// List of meditation prompts
self.prompts = ["Breathe deeply...", "Focus on your breath...", "Let go of your thoughts...", "Feel the peace within...", "You are present...", "Allow yourself to be...", "Feel the energy flow...", "Find your center..."];
self.currentPrompt = 0;
self.promptTimer = 0;
self.promptDuration = 300; // 5 seconds per prompt
// Update method to cycle through prompts
self.update = function () {
self.promptTimer++;
// Change prompt periodically
if (self.promptTimer >= self.promptDuration) {
self.currentPrompt = (self.currentPrompt + 1) % self.prompts.length;
self.promptText.setText(self.prompts[self.currentPrompt]);
self.promptTimer = 0;
// Animate text appearance
self.promptText.alpha = 0;
tween(self.promptText, {
alpha: 1
}, {
duration: 1000
});
}
// Subtle pulse animation
var scale = 1 + 0.05 * Math.sin(self.promptTimer * 0.01);
self.promptText.scale.x = scale;
self.promptText.scale.y = scale;
};
return self;
});
var MeditationSpace = Container.expand(function () {
var self = Container.call(this);
// Create a peaceful gradient background
var background = new Container();
self.addChild(background);
// Create calming gradient colors for the background
var skyColors = [0x081832, 0x112a4e, 0x1a3c6a, 0x234e86, 0x2c60a2];
var bandHeight = 2732 / skyColors.length;
for (var i = 0; i < skyColors.length; i++) {
var skyBand = LK.getAsset('skyBand' + i, {
width: 2048,
height: bandHeight,
color: skyColors[i],
y: i * bandHeight
});
background.addChild(skyBand);
}
// Create stars
var stars = new Container();
self.stars = stars;
self.addChild(stars);
// Add stars to the night sky
var starCount = 100;
for (var i = 0; i < starCount; i++) {
var starSize = 2 + Math.random() * 4;
var star = LK.getAsset('star_' + i, {
width: starSize,
height: starSize,
color: 0xffffff,
shape: 'ellipse',
x: Math.random() * 2048,
y: Math.random() * 1200,
alpha: 0.5 + Math.random() * 0.5
});
star.pulseSpeed = 0.01 + Math.random() * 0.02;
star.pulsePhase = Math.random() * Math.PI * 2;
stars.addChild(star);
}
// Add floating lotus flowers
var lotusFlowers = new Container();
self.lotusFlowers = lotusFlowers;
self.addChild(lotusFlowers);
var lotusCount = 5;
for (var i = 0; i < lotusCount; i++) {
var lotusSize = 80 + Math.random() * 40;
var lotus = LK.getAsset('lotus_' + i, {
width: lotusSize,
height: lotusSize,
color: 0xffb6c1,
shape: 'ellipse',
x: 400 + Math.random() * 1200,
y: 1500 + Math.random() * 800
});
lotus.floatSpeed = 0.2 + Math.random() * 0.3;
lotus.floatPhase = Math.random() * Math.PI * 2;
lotusFlowers.addChild(lotus);
}
// Add water ripples
var ripples = new Container();
self.ripples = ripples;
self.addChild(ripples);
self.rippleTimer = 0;
// Update function to animate elements
self.update = function () {
// Animate stars (pulsing effect)
for (var i = 0; i < stars.children.length; i++) {
var star = stars.children[i];
star.pulsePhase += star.pulseSpeed;
star.alpha = 0.5 + 0.5 * Math.sin(star.pulsePhase);
}
// Animate lotus flowers (gentle floating)
for (var i = 0; i < lotusFlowers.children.length; i++) {
var lotus = lotusFlowers.children[i];
lotus.floatPhase += 0.01;
lotus.y += Math.sin(lotus.floatPhase) * lotus.floatSpeed;
}
// Create new ripples periodically
self.rippleTimer++;
if (self.rippleTimer > 120) {
// Every 2 seconds
self.createRipple(400 + Math.random() * 1200, 1500 + Math.random() * 800);
self.rippleTimer = 0;
}
// Update existing ripples
for (var i = ripples.children.length - 1; i >= 0; i--) {
var ripple = ripples.children[i];
ripple.age++;
// Expand ripple
ripple.scale.x += 0.01;
ripple.scale.y += 0.01;
// Fade out ripple
ripple.alpha = 1 - ripple.age / ripple.lifespan;
// Remove old ripples
if (ripple.age >= ripple.lifespan) {
ripple.destroy();
ripples.removeChild(ripple);
}
}
};
// Method to create a new water ripple
self.createRipple = function (x, y) {
var ripple = LK.getAsset('ripple', {
width: 50,
height: 50,
color: 0xffffff,
shape: 'ellipse',
x: x,
y: y,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
ripple.age = 0;
ripple.lifespan = 120; // 2 seconds
ripples.addChild(ripple);
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.health = 100;
self.maxHealth = 100;
self.shootCooldown = 0;
self.shootDelay = 15; // Frames between shots
self.canMove = true;
// Health bar setup
var healthBarBg = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0,
y: 120
});
var healthBarFill = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0,
y: 122
});
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
healthBarFill.scale.x = healthPercentage;
// Center the health bar
healthBarFill.x = -100 * (1 - healthPercentage);
};
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
self.updateHealthBar();
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y - 40;
// Add to game's bullets array
bullets.push(bullet);
game.addChild(bullet);
// Play gun sound
LK.getSound('gunshot').play();
// Reset cooldown
self.shootCooldown = self.shootDelay;
}
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.down = function (x, y, obj) {
self.shoot();
};
// Initialize health bar
self.updateHealthBar();
return self;
});
var SoulOrb = Container.expand(function () {
var self = Container.call(this);
// Create the soul orb with a glowing effect
var orbCore = self.attachAsset('centerCircle', {
width: 60,
height: 60,
color: 0x83de44,
anchorX: 0.5,
anchorY: 0.5
});
// Create outer glow
var orbGlow = LK.getAsset('centerCircle', {
width: 100,
height: 100,
color: 0xb8ffb0,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.addChild(orbGlow);
// Add pulsating animation to the glow
self.glowPhase = 0;
self.energy = 0; // Energy level (0-100)
self.maxEnergy = 100;
self.pulseSpeed = 0.05;
// Update method to animate the orb
self.update = function () {
self.glowPhase += self.pulseSpeed;
orbGlow.scale.x = 1 + 0.2 * Math.sin(self.glowPhase);
orbGlow.scale.y = 1 + 0.2 * Math.sin(self.glowPhase);
// Adjust color based on energy level
var energyRatio = self.energy / self.maxEnergy;
var r = Math.floor(131 + (255 - 131) * energyRatio);
var g = Math.floor(222 + (255 - 222) * energyRatio);
var b = Math.floor(68 + (255 - 68) * energyRatio);
// Convert RGB to hex
var color = r << 16 | g << 8 | b;
orbCore.tint = color;
};
// Method to add energy to the orb
self.addEnergy = function (amount) {
self.energy = Math.min(self.maxEnergy, self.energy + amount);
// Flash with increasing brightness as energy increases
LK.effects.flashObject(self, 0xffffff, 300);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x081832
});
/****
* Game Code
****/
// Game variables
var meditationSpace;
var soulOrb;
var breathingCircle;
var meditationGuide;
var gameActive = true;
var dragOffset = {
x: 0,
y: 0
};
var meditationLevel = 1;
var meditationTime = 0;
var soulEnergy = 0;
var maxSoulEnergy = 100;
var breathingTimer = 0;
// Meditation practices
var practices = [{
id: 'breathe',
text: 'Complete 5 breathing cycles',
count: 0,
target: 5,
completed: false
}, {
id: 'focus',
text: 'Maintain focus for 60 seconds',
count: 0,
target: 60,
completed: false
}, {
id: 'energy',
text: 'Fill your soul with energy',
count: 0,
target: 100,
completed: false
}];
// Setup practice UI
var practiceContainer = new Container();
practiceContainer.x = 40;
practiceContainer.y = 180;
LK.gui.left.addChild(practiceContainer);
var practiceTitle = new Text2('PRACTICES:', {
size: 50,
fill: 0xFFFFFF
});
practiceContainer.addChild(practiceTitle);
// Create text for each practice
var practiceTexts = [];
for (var i = 0; i < practices.length; i++) {
var practiceText = new Text2('• ' + practices[i].text + ' (0/' + practices[i].target + ')', {
size: 40,
fill: 0xCCCCCC
});
practiceText.y = 60 + i * 50;
practiceContainer.addChild(practiceText);
practiceTexts.push(practiceText);
}
// Function to update practice UI
function updatePracticeDisplay() {
for (var i = 0; i < practices.length; i++) {
var practice = practices[i];
var color = practice.completed ? 0x83de44 : 0xCCCCCC;
practiceTexts[i].setText('• ' + practice.text + ' (' + practice.count + '/' + practice.target + ')');
practiceTexts[i].fill = color;
}
}
// Setup UI
var energyTxt = new Text2('Soul Energy: 0%', {
size: 60,
fill: 0xFFFFFF
});
energyTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(energyTxt);
var levelTxt = new Text2('Meditation Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 70;
LK.gui.top.addChild(levelTxt);
// Create and add meditation space
meditationSpace = new MeditationSpace();
game.addChild(meditationSpace);
// Initialize soul orb
soulOrb = new SoulOrb();
soulOrb.x = 2048 / 2;
soulOrb.y = 2732 / 3;
game.addChild(soulOrb);
// Initialize breathing circle
breathingCircle = new BreathingCircle();
breathingCircle.x = 2048 / 2;
breathingCircle.y = 2732 / 2 + 200;
game.addChild(breathingCircle);
// Initialize meditation guide
meditationGuide = new MeditationGuide();
meditationGuide.x = 2048 / 2;
meditationGuide.y = 2732 / 5;
game.addChild(meditationGuide);
// Play meditation music
LK.playMusic('background_music');
// Create a ripple at a position (utility function)
function createRipple(x, y) {
meditationSpace.createRipple(x, y);
}
// Add soul energy
function addSoulEnergy(amount) {
soulOrb.addEnergy(amount);
// Update energy practice
var energyPractice = practices.find(function (practice) {
return practice.id === 'energy';
});
if (energyPractice && !energyPractice.completed) {
energyPractice.count = Math.floor(soulOrb.energy);
if (energyPractice.count >= energyPractice.target) {
energyPractice.completed = true;
LK.effects.flashObject(practiceTexts[2], 0x83de44, 1000);
}
updatePracticeDisplay();
}
}
// Check if meditation level should increase
function checkMeditationProgress() {
// Check if practices are completed to progress meditation level
var completedPractices = practices.filter(function (practice) {
return practice.completed;
}).length;
var newLevel = 1 + Math.floor(completedPractices / 2);
if (newLevel > meditationLevel) {
meditationLevel = newLevel;
levelTxt.setText('Meditation Level: ' + meditationLevel);
// Bonus for reaching new level
addSoulEnergy(10 * meditationLevel);
// Visual feedback
LK.effects.flashScreen(0x83de44, 500);
}
}
// Meditation interaction handling
game.down = function (x, y, obj) {
// Calculate distances to interactive elements
var distToOrb = Math.sqrt(Math.pow(x - soulOrb.x, 2) + Math.pow(y - soulOrb.y, 2));
// If touching the soul orb
if (distToOrb < 100) {
// Add energy to the soul
soulOrb.addEnergy(5);
// Emit a ripple effect from the interaction point
meditationSpace.createRipple(x, y);
// Create visual feedback
LK.effects.flashObject(soulOrb, 0xFFFFFF, 500);
} else {
// Create ripple at touch point
meditationSpace.createRipple(x, y);
}
// Set drag offset for moving the orb
dragOffset.x = soulOrb.x - x;
dragOffset.y = soulOrb.y - y;
};
game.move = function (x, y, obj) {
// Move soul orb with drag
soulOrb.x = x + dragOffset.x;
soulOrb.y = y + dragOffset.y;
// Keep soul orb in bounds
soulOrb.x = Math.max(100, Math.min(2048 - 100, soulOrb.x));
soulOrb.y = Math.max(100, Math.min(2732 - 100, soulOrb.y));
// Create subtle ripple effects during movement
if (Math.random() < 0.1) {
meditationSpace.createRipple(soulOrb.x + (Math.random() * 100 - 50), soulOrb.y + (Math.random() * 100 - 50));
}
};
game.up = function () {
// Create final ripple at release point
meditationSpace.createRipple(soulOrb.x, soulOrb.y);
};
// Game update loop
game.update = function () {
if (!gameActive) return;
// Update meditation space
meditationSpace.update();
// Update soul orb, breathing circle, and guide
soulOrb.update();
breathingCircle.update();
meditationGuide.update();
// Update energy display
energyTxt.setText('Soul Energy: ' + Math.floor(soulOrb.energy / maxSoulEnergy * 100) + '%');
// Track breathing cycles for breathe practice
if (breathingCircle.breathPhase === 3 && breathingCircle.breathTimer === 0) {
// Just completed a full breathing cycle
var breathePractice = practices.find(function (practice) {
return practice.id === 'breathe';
});
if (breathePractice && !breathePractice.completed) {
breathePractice.count++;
if (breathePractice.count >= breathePractice.target) {
breathePractice.completed = true;
LK.effects.flashObject(practiceTexts[0], 0x83de44, 1000);
// Add soul energy when completing breathing practice
soulOrb.addEnergy(20);
}
updatePracticeDisplay();
}
}
// Track focus time for focus practice
meditationTime++;
if (meditationTime % 60 === 0) {
// Every second
var focusPractice = practices.find(function (practice) {
return practice.id === 'focus';
});
if (focusPractice && !focusPractice.completed) {
focusPractice.count++;
if (focusPractice.count >= focusPractice.target) {
focusPractice.completed = true;
LK.effects.flashObject(practiceTexts[1], 0x83de44, 1000);
// Add soul energy when completing focus practice
soulOrb.addEnergy(30);
}
updatePracticeDisplay();
}
}
// Add soul energy over time with active meditation
if (meditationTime % 180 === 0) {
// Every 3 seconds
soulOrb.addEnergy(1);
// Track soul energy for energy practice
var energyPractice = practices.find(function (practice) {
return practice.id === 'energy';
});
if (energyPractice && !energyPractice.completed) {
energyPractice.count = Math.floor(soulOrb.energy);
if (energyPractice.count >= energyPractice.target) {
energyPractice.completed = true;
LK.effects.flashObject(practiceTexts[2], 0x83de44, 1000);
}
updatePracticeDisplay();
}
}
// Check for meditation level up
if (meditationTime % 600 === 0) {
// Every 10 seconds
meditationLevel++;
levelTxt.setText('Meditation Level: ' + meditationLevel);
// Visual feedback for level up
LK.effects.flashScreen(0x83de44, 500);
}
// Check if all practices are completed
var allPracticesCompleted = practices.every(function (practice) {
return practice.completed;
});
if (allPracticesCompleted && gameActive) {
// Show enlightenment message
var enlightenmentMessage = new Text2('ENLIGHTENMENT ACHIEVED!', {
size: 100,
fill: 0xFFFFFF
});
enlightenmentMessage.anchor.set(0.5, 0.5);
enlightenmentMessage.x = 2048 / 2;
enlightenmentMessage.y = 2732 / 2;
game.addChild(enlightenmentMessage);
// Visual effects for enlightenment
LK.effects.flashScreen(0xFFFFFF, 2000);
// Create new practices with higher requirements
practices = [{
id: 'breathe',
text: 'Complete 10 breathing cycles',
count: 0,
target: 10,
completed: false
}, {
id: 'focus',
text: 'Maintain focus for 120 seconds',
count: 0,
target: 120,
completed: false
}, {
id: 'energy',
text: 'Achieve deeper enlightenment',
count: 0,
target: 200,
completed: false
}];
// Update practice display
for (var i = 0; i < practices.length; i++) {
practiceTexts[i].setText('• ' + practices[i].text + ' (0/' + practices[i].target + ')');
practiceTexts[i].fill = 0xCCCCCC;
}
// Increase soul orb capacity
maxSoulEnergy = 200;
// Remove the enlightenment message after 3 seconds
LK.setTimeout(function () {
enlightenmentMessage.destroy();
updatePracticeDisplay();
}, 3000);
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Background = Container.expand(function () {
var self = Container.call(this);
// Create sky gradient
var skyGradient = new Container();
// Create several bands of sky color
var skyColors = [0x1a3c80, 0x3a5c9f, 0x5a7cbf, 0x7a9cdf, 0x9abcff];
var bandHeight = 2732 / skyColors.length;
for (var i = 0; i < skyColors.length; i++) {
var skyBand = LK.getAsset('skyBand' + i, {
width: 2048,
height: bandHeight,
color: skyColors[i],
shape: 'box',
y: i * bandHeight
});
skyGradient.addChild(skyBand);
}
self.addChild(skyGradient);
// Create buildings
var buildings = new Container();
self.addChild(buildings);
// Far buildings (darker)
var buildingColors = [0x222a40, 0x323a50, 0x424a60];
var buildingCount = 15;
for (var i = 0; i < buildingCount; i++) {
var buildingWidth = 100 + Math.random() * 150;
var buildingHeight = 300 + Math.random() * 400;
var building = LK.getAsset('building_far_' + i, {
width: buildingWidth,
height: buildingHeight,
color: buildingColors[Math.floor(Math.random() * buildingColors.length)],
shape: 'box',
x: i * (2048 / buildingCount),
y: 2732 - buildingHeight,
anchorY: 0
});
buildings.addChild(building);
}
// Near buildings (slightly lighter)
var nearBuildingColors = [0x303a50, 0x404a60, 0x505a70];
var nearBuildingCount = 10;
for (var i = 0; i < nearBuildingCount; i++) {
var buildingWidth = 180 + Math.random() * 150;
var buildingHeight = 500 + Math.random() * 600;
var building = LK.getAsset('building_near_' + i, {
width: buildingWidth,
height: buildingHeight,
color: nearBuildingColors[Math.floor(Math.random() * nearBuildingColors.length)],
shape: 'box',
x: i * (2048 / nearBuildingCount),
y: 2732 - buildingHeight,
anchorY: 0
});
buildings.addChild(building);
}
// Add some windows to buildings (as small yellow rectangles)
for (var b = 0; b < buildings.children.length; b++) {
var building = buildings.children[b];
var windowCount = Math.floor(building.width / 30) * Math.floor(building.height / 40);
// Limit window count for performance
windowCount = Math.min(windowCount, 20);
for (var w = 0; w < windowCount; w++) {
var windowSize = 10 + Math.random() * 10;
var windowX = Math.random() * (building.width - windowSize);
var windowY = Math.random() * (building.height - windowSize);
// Only add some windows (randomized lighting effect)
if (Math.random() > 0.4) {
var windowLight = LK.getAsset('window_' + b + '_' + w, {
width: windowSize,
height: windowSize,
color: Math.random() > 0.7 ? 0xffcc66 : 0x888888,
// Some windows lit, some not
shape: 'box',
x: building.x + windowX,
y: building.y + windowY
});
self.addChild(windowLight);
}
}
}
// Add clouds
var clouds = new Container();
self.clouds = clouds;
self.addChild(clouds);
var cloudCount = 6;
for (var i = 0; i < cloudCount; i++) {
var cloudWidth = 200 + Math.random() * 300;
var cloudHeight = 100 + Math.random() * 100;
var cloud = LK.getAsset('cloud_' + i, {
width: cloudWidth,
height: cloudHeight,
color: 0xddddff,
shape: 'ellipse',
x: Math.random() * 2048,
y: 200 + Math.random() * 400,
alpha: 0.7
});
// Add some variation to clouds with slight scaling
cloud.scaleX = 0.8 + Math.random() * 0.4;
cloud.scaleY = 0.8 + Math.random() * 0.4;
// Store cloud speed for animation
cloud.speedX = 0.2 + Math.random() * 0.3;
clouds.addChild(cloud);
}
// Update function to animate clouds
self.update = function () {
// Move clouds horizontally
for (var i = 0; i < clouds.children.length; i++) {
var cloud = clouds.children[i];
cloud.x += cloud.speedX;
// Loop clouds when they move off screen
if (cloud.x > 2048 + cloud.width) {
cloud.x = -cloud.width;
cloud.y = 200 + Math.random() * 400;
}
}
};
return self;
});
var BreathingCircle = Container.expand(function () {
var self = Container.call(this);
// Create the breathing circle
var innerCircle = self.attachAsset('centerCircle', {
width: 200,
height: 200,
color: 0x3a5c9f,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4
});
var outerCircle = LK.getAsset('centerCircle', {
width: 240,
height: 240,
color: 0x5a7cbf,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2
});
self.addChild(outerCircle);
// Breathing animation state
self.breathPhase = 0; // 0: inhale growing, 1: hold, 2: exhale shrinking, 3: hold
self.breathTimer = 0;
self.inhaleDuration = 240; // 4 seconds
self.holdDuration = 120; // 2 seconds
self.exhaleDuration = 240; // 4 seconds
self.restDuration = 60; // 1 second
// Text guide for breathing
self.breathText = new Text2("Breathe In", {
size: 40,
fill: 0xffffff
});
self.breathText.anchor.set(0.5, 0.5);
self.breathText.y = -160;
self.addChild(self.breathText);
// Update method to animate the breathing circle
self.update = function () {
self.breathTimer++;
// Determine current phase based on timer
if (self.breathPhase === 0) {
// Inhale
var progress = self.breathTimer / self.inhaleDuration;
var scale = 1 + progress * 0.5;
innerCircle.scale.x = innerCircle.scale.y = scale;
outerCircle.scale.x = outerCircle.scale.y = 0.9 + progress * 0.5;
self.breathText.setText("Breathe In");
if (self.breathTimer >= self.inhaleDuration) {
self.breathPhase = 1;
self.breathTimer = 0;
}
} else if (self.breathPhase === 1) {
// Hold after inhale
self.breathText.setText("Hold");
if (self.breathTimer >= self.holdDuration) {
self.breathPhase = 2;
self.breathTimer = 0;
}
} else if (self.breathPhase === 2) {
// Exhale
var progress = self.breathTimer / self.exhaleDuration;
var scale = 1.5 - progress * 0.5;
innerCircle.scale.x = innerCircle.scale.y = scale;
outerCircle.scale.x = outerCircle.scale.y = 1.4 - progress * 0.5;
self.breathText.setText("Breathe Out");
if (self.breathTimer >= self.exhaleDuration) {
self.breathPhase = 3;
self.breathTimer = 0;
}
} else {
// Hold after exhale
self.breathText.setText("Rest");
if (self.breathTimer >= self.restDuration) {
self.breathPhase = 0;
self.breathTimer = 0;
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.direction = {
x: 0,
y: -1
}; // Default upward
self.lifespan = 120; // 2 seconds at 60fps
self.age = 0;
self.damage = 1;
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
self.age++;
if (self.age > self.lifespan) {
self.markForDeletion = true;
}
};
return self;
});
var Criminal = Container.expand(function () {
var self = Container.call(this);
var criminalGraphics = self.attachAsset('criminal', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 2; // Reduced from 3
self.shootCooldown = 0;
self.shootDelay = 120; // Frames between shots
self.attackRange = 300; // Reduced from 500
self.movePattern = Math.floor(Math.random() * 3); // 0: direct, 1: zigzag, 2: circle
self.moveTimer = 0;
self.update = function () {
// Target the player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Movement based on pattern
if (distance > 100) {
// Keep some distance
var moveX = 0;
var moveY = 0;
switch (self.movePattern) {
case 0:
// Direct
moveX = dx / distance;
moveY = dy / distance;
break;
case 1:
// Zigzag
self.moveTimer++;
moveX = dx / distance + Math.sin(self.moveTimer * 0.1) * 0.5;
moveY = dy / distance;
break;
case 2:
// Circle
self.moveTimer++;
moveX = dx / distance + Math.sin(self.moveTimer * 0.05) * 0.8;
moveY = dy / distance + Math.cos(self.moveTimer * 0.05) * 0.8;
break;
}
self.x += moveX * self.speed;
self.y += moveY * self.speed;
}
// Shooting logic
if (distance < self.attackRange) {
if (self.shootCooldown <= 0) {
// Create enemy bullet
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction toward player
var dirX = dx / distance;
var dirY = dy / distance;
bullet.direction = {
x: dirX,
y: dirY
};
// Mark as enemy bullet
bullet.isEnemyBullet = true;
// Add to game's bullets array
bullets.push(bullet);
game.addChild(bullet);
// Reset cooldown with some randomness
self.shootCooldown = self.shootDelay + Math.random() * 60;
}
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function () {
self.health--;
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
LK.getSound('criminal_down').play();
LK.setScore(LK.getScore() + 1);
self.markForDeletion = true;
// Track criminal elimination task
var eliminateTask = tasks.find(function (task) {
return task.id === 'eliminate';
});
if (eliminateTask && !eliminateTask.completed) {
eliminateTask.count++;
if (eliminateTask.count >= eliminateTask.target) {
eliminateTask.completed = true;
LK.effects.flashObject(taskTexts[1], 0x00FF00, 1000);
}
updateTaskDisplay();
}
}
};
return self;
});
var EmergencyCall = Container.expand(function () {
var self = Container.call(this);
var callGraphics = self.attachAsset('emergencyCall', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 600; // 10 seconds at 60fps
self.age = 0;
self.flashInterval = 30;
self.update = function () {
self.age++;
// Flash effect
if (self.age % self.flashInterval === 0) {
if (callGraphics.alpha === 1) {
tween(callGraphics, {
alpha: 0.3
}, {
duration: 500
});
} else {
tween(callGraphics, {
alpha: 1
}, {
duration: 500
});
}
}
// Expire after lifetime
if (self.age > self.lifetime) {
self.markForDeletion = true;
}
};
self.down = function () {
// Responding to call
LK.getSound('radiochatter').play();
// Spawn criminals from top of screen, using emergency call's X position
spawnCriminals(self.x, 0, 2 + Math.floor(Math.random() * 3));
self.markForDeletion = true;
// Increase score for responding
LK.setScore(LK.getScore() + 2);
// Track emergency call response task
var respondTask = tasks.find(function (task) {
return task.id === 'respond';
});
if (respondTask && !respondTask.completed) {
respondTask.count++;
if (respondTask.count >= respondTask.target) {
respondTask.completed = true;
LK.effects.flashObject(taskTexts[0], 0x00FF00, 1000);
}
updateTaskDisplay();
}
};
return self;
});
var MeditationGuide = Container.expand(function () {
var self = Container.call(this);
// Create a text display for meditation prompts
self.promptText = new Text2("Breathe deeply...", {
size: 60,
fill: 0xffffff,
align: 'center'
});
self.promptText.anchor.set(0.5, 0.5);
self.addChild(self.promptText);
// List of meditation prompts
self.prompts = ["Breathe deeply...", "Focus on your breath...", "Let go of your thoughts...", "Feel the peace within...", "You are present...", "Allow yourself to be...", "Feel the energy flow...", "Find your center..."];
self.currentPrompt = 0;
self.promptTimer = 0;
self.promptDuration = 300; // 5 seconds per prompt
// Update method to cycle through prompts
self.update = function () {
self.promptTimer++;
// Change prompt periodically
if (self.promptTimer >= self.promptDuration) {
self.currentPrompt = (self.currentPrompt + 1) % self.prompts.length;
self.promptText.setText(self.prompts[self.currentPrompt]);
self.promptTimer = 0;
// Animate text appearance
self.promptText.alpha = 0;
tween(self.promptText, {
alpha: 1
}, {
duration: 1000
});
}
// Subtle pulse animation
var scale = 1 + 0.05 * Math.sin(self.promptTimer * 0.01);
self.promptText.scale.x = scale;
self.promptText.scale.y = scale;
};
return self;
});
var MeditationSpace = Container.expand(function () {
var self = Container.call(this);
// Create a peaceful gradient background
var background = new Container();
self.addChild(background);
// Create calming gradient colors for the background
var skyColors = [0x081832, 0x112a4e, 0x1a3c6a, 0x234e86, 0x2c60a2];
var bandHeight = 2732 / skyColors.length;
for (var i = 0; i < skyColors.length; i++) {
var skyBand = LK.getAsset('skyBand' + i, {
width: 2048,
height: bandHeight,
color: skyColors[i],
y: i * bandHeight
});
background.addChild(skyBand);
}
// Create stars
var stars = new Container();
self.stars = stars;
self.addChild(stars);
// Add stars to the night sky
var starCount = 100;
for (var i = 0; i < starCount; i++) {
var starSize = 2 + Math.random() * 4;
var star = LK.getAsset('star_' + i, {
width: starSize,
height: starSize,
color: 0xffffff,
shape: 'ellipse',
x: Math.random() * 2048,
y: Math.random() * 1200,
alpha: 0.5 + Math.random() * 0.5
});
star.pulseSpeed = 0.01 + Math.random() * 0.02;
star.pulsePhase = Math.random() * Math.PI * 2;
stars.addChild(star);
}
// Add floating lotus flowers
var lotusFlowers = new Container();
self.lotusFlowers = lotusFlowers;
self.addChild(lotusFlowers);
var lotusCount = 5;
for (var i = 0; i < lotusCount; i++) {
var lotusSize = 80 + Math.random() * 40;
var lotus = LK.getAsset('lotus_' + i, {
width: lotusSize,
height: lotusSize,
color: 0xffb6c1,
shape: 'ellipse',
x: 400 + Math.random() * 1200,
y: 1500 + Math.random() * 800
});
lotus.floatSpeed = 0.2 + Math.random() * 0.3;
lotus.floatPhase = Math.random() * Math.PI * 2;
lotusFlowers.addChild(lotus);
}
// Add water ripples
var ripples = new Container();
self.ripples = ripples;
self.addChild(ripples);
self.rippleTimer = 0;
// Update function to animate elements
self.update = function () {
// Animate stars (pulsing effect)
for (var i = 0; i < stars.children.length; i++) {
var star = stars.children[i];
star.pulsePhase += star.pulseSpeed;
star.alpha = 0.5 + 0.5 * Math.sin(star.pulsePhase);
}
// Animate lotus flowers (gentle floating)
for (var i = 0; i < lotusFlowers.children.length; i++) {
var lotus = lotusFlowers.children[i];
lotus.floatPhase += 0.01;
lotus.y += Math.sin(lotus.floatPhase) * lotus.floatSpeed;
}
// Create new ripples periodically
self.rippleTimer++;
if (self.rippleTimer > 120) {
// Every 2 seconds
self.createRipple(400 + Math.random() * 1200, 1500 + Math.random() * 800);
self.rippleTimer = 0;
}
// Update existing ripples
for (var i = ripples.children.length - 1; i >= 0; i--) {
var ripple = ripples.children[i];
ripple.age++;
// Expand ripple
ripple.scale.x += 0.01;
ripple.scale.y += 0.01;
// Fade out ripple
ripple.alpha = 1 - ripple.age / ripple.lifespan;
// Remove old ripples
if (ripple.age >= ripple.lifespan) {
ripple.destroy();
ripples.removeChild(ripple);
}
}
};
// Method to create a new water ripple
self.createRipple = function (x, y) {
var ripple = LK.getAsset('ripple', {
width: 50,
height: 50,
color: 0xffffff,
shape: 'ellipse',
x: x,
y: y,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
ripple.age = 0;
ripple.lifespan = 120; // 2 seconds
ripples.addChild(ripple);
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.health = 100;
self.maxHealth = 100;
self.shootCooldown = 0;
self.shootDelay = 15; // Frames between shots
self.canMove = true;
// Health bar setup
var healthBarBg = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0,
y: 120
});
var healthBarFill = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0,
y: 122
});
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
healthBarFill.scale.x = healthPercentage;
// Center the health bar
healthBarFill.x = -100 * (1 - healthPercentage);
};
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
self.updateHealthBar();
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y - 40;
// Add to game's bullets array
bullets.push(bullet);
game.addChild(bullet);
// Play gun sound
LK.getSound('gunshot').play();
// Reset cooldown
self.shootCooldown = self.shootDelay;
}
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.down = function (x, y, obj) {
self.shoot();
};
// Initialize health bar
self.updateHealthBar();
return self;
});
var SoulOrb = Container.expand(function () {
var self = Container.call(this);
// Create the soul orb with a glowing effect
var orbCore = self.attachAsset('centerCircle', {
width: 60,
height: 60,
color: 0x83de44,
anchorX: 0.5,
anchorY: 0.5
});
// Create outer glow
var orbGlow = LK.getAsset('centerCircle', {
width: 100,
height: 100,
color: 0xb8ffb0,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.addChild(orbGlow);
// Add pulsating animation to the glow
self.glowPhase = 0;
self.energy = 0; // Energy level (0-100)
self.maxEnergy = 100;
self.pulseSpeed = 0.05;
// Update method to animate the orb
self.update = function () {
self.glowPhase += self.pulseSpeed;
orbGlow.scale.x = 1 + 0.2 * Math.sin(self.glowPhase);
orbGlow.scale.y = 1 + 0.2 * Math.sin(self.glowPhase);
// Adjust color based on energy level
var energyRatio = self.energy / self.maxEnergy;
var r = Math.floor(131 + (255 - 131) * energyRatio);
var g = Math.floor(222 + (255 - 222) * energyRatio);
var b = Math.floor(68 + (255 - 68) * energyRatio);
// Convert RGB to hex
var color = r << 16 | g << 8 | b;
orbCore.tint = color;
};
// Method to add energy to the orb
self.addEnergy = function (amount) {
self.energy = Math.min(self.maxEnergy, self.energy + amount);
// Flash with increasing brightness as energy increases
LK.effects.flashObject(self, 0xffffff, 300);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x081832
});
/****
* Game Code
****/
// Game variables
var meditationSpace;
var soulOrb;
var breathingCircle;
var meditationGuide;
var gameActive = true;
var dragOffset = {
x: 0,
y: 0
};
var meditationLevel = 1;
var meditationTime = 0;
var soulEnergy = 0;
var maxSoulEnergy = 100;
var breathingTimer = 0;
// Meditation practices
var practices = [{
id: 'breathe',
text: 'Complete 5 breathing cycles',
count: 0,
target: 5,
completed: false
}, {
id: 'focus',
text: 'Maintain focus for 60 seconds',
count: 0,
target: 60,
completed: false
}, {
id: 'energy',
text: 'Fill your soul with energy',
count: 0,
target: 100,
completed: false
}];
// Setup practice UI
var practiceContainer = new Container();
practiceContainer.x = 40;
practiceContainer.y = 180;
LK.gui.left.addChild(practiceContainer);
var practiceTitle = new Text2('PRACTICES:', {
size: 50,
fill: 0xFFFFFF
});
practiceContainer.addChild(practiceTitle);
// Create text for each practice
var practiceTexts = [];
for (var i = 0; i < practices.length; i++) {
var practiceText = new Text2('• ' + practices[i].text + ' (0/' + practices[i].target + ')', {
size: 40,
fill: 0xCCCCCC
});
practiceText.y = 60 + i * 50;
practiceContainer.addChild(practiceText);
practiceTexts.push(practiceText);
}
// Function to update practice UI
function updatePracticeDisplay() {
for (var i = 0; i < practices.length; i++) {
var practice = practices[i];
var color = practice.completed ? 0x83de44 : 0xCCCCCC;
practiceTexts[i].setText('• ' + practice.text + ' (' + practice.count + '/' + practice.target + ')');
practiceTexts[i].fill = color;
}
}
// Setup UI
var energyTxt = new Text2('Soul Energy: 0%', {
size: 60,
fill: 0xFFFFFF
});
energyTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(energyTxt);
var levelTxt = new Text2('Meditation Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 70;
LK.gui.top.addChild(levelTxt);
// Create and add meditation space
meditationSpace = new MeditationSpace();
game.addChild(meditationSpace);
// Initialize soul orb
soulOrb = new SoulOrb();
soulOrb.x = 2048 / 2;
soulOrb.y = 2732 / 3;
game.addChild(soulOrb);
// Initialize breathing circle
breathingCircle = new BreathingCircle();
breathingCircle.x = 2048 / 2;
breathingCircle.y = 2732 / 2 + 200;
game.addChild(breathingCircle);
// Initialize meditation guide
meditationGuide = new MeditationGuide();
meditationGuide.x = 2048 / 2;
meditationGuide.y = 2732 / 5;
game.addChild(meditationGuide);
// Play meditation music
LK.playMusic('background_music');
// Create a ripple at a position (utility function)
function createRipple(x, y) {
meditationSpace.createRipple(x, y);
}
// Add soul energy
function addSoulEnergy(amount) {
soulOrb.addEnergy(amount);
// Update energy practice
var energyPractice = practices.find(function (practice) {
return practice.id === 'energy';
});
if (energyPractice && !energyPractice.completed) {
energyPractice.count = Math.floor(soulOrb.energy);
if (energyPractice.count >= energyPractice.target) {
energyPractice.completed = true;
LK.effects.flashObject(practiceTexts[2], 0x83de44, 1000);
}
updatePracticeDisplay();
}
}
// Check if meditation level should increase
function checkMeditationProgress() {
// Check if practices are completed to progress meditation level
var completedPractices = practices.filter(function (practice) {
return practice.completed;
}).length;
var newLevel = 1 + Math.floor(completedPractices / 2);
if (newLevel > meditationLevel) {
meditationLevel = newLevel;
levelTxt.setText('Meditation Level: ' + meditationLevel);
// Bonus for reaching new level
addSoulEnergy(10 * meditationLevel);
// Visual feedback
LK.effects.flashScreen(0x83de44, 500);
}
}
// Meditation interaction handling
game.down = function (x, y, obj) {
// Calculate distances to interactive elements
var distToOrb = Math.sqrt(Math.pow(x - soulOrb.x, 2) + Math.pow(y - soulOrb.y, 2));
// If touching the soul orb
if (distToOrb < 100) {
// Add energy to the soul
soulOrb.addEnergy(5);
// Emit a ripple effect from the interaction point
meditationSpace.createRipple(x, y);
// Create visual feedback
LK.effects.flashObject(soulOrb, 0xFFFFFF, 500);
} else {
// Create ripple at touch point
meditationSpace.createRipple(x, y);
}
// Set drag offset for moving the orb
dragOffset.x = soulOrb.x - x;
dragOffset.y = soulOrb.y - y;
};
game.move = function (x, y, obj) {
// Move soul orb with drag
soulOrb.x = x + dragOffset.x;
soulOrb.y = y + dragOffset.y;
// Keep soul orb in bounds
soulOrb.x = Math.max(100, Math.min(2048 - 100, soulOrb.x));
soulOrb.y = Math.max(100, Math.min(2732 - 100, soulOrb.y));
// Create subtle ripple effects during movement
if (Math.random() < 0.1) {
meditationSpace.createRipple(soulOrb.x + (Math.random() * 100 - 50), soulOrb.y + (Math.random() * 100 - 50));
}
};
game.up = function () {
// Create final ripple at release point
meditationSpace.createRipple(soulOrb.x, soulOrb.y);
};
// Game update loop
game.update = function () {
if (!gameActive) return;
// Update meditation space
meditationSpace.update();
// Update soul orb, breathing circle, and guide
soulOrb.update();
breathingCircle.update();
meditationGuide.update();
// Update energy display
energyTxt.setText('Soul Energy: ' + Math.floor(soulOrb.energy / maxSoulEnergy * 100) + '%');
// Track breathing cycles for breathe practice
if (breathingCircle.breathPhase === 3 && breathingCircle.breathTimer === 0) {
// Just completed a full breathing cycle
var breathePractice = practices.find(function (practice) {
return practice.id === 'breathe';
});
if (breathePractice && !breathePractice.completed) {
breathePractice.count++;
if (breathePractice.count >= breathePractice.target) {
breathePractice.completed = true;
LK.effects.flashObject(practiceTexts[0], 0x83de44, 1000);
// Add soul energy when completing breathing practice
soulOrb.addEnergy(20);
}
updatePracticeDisplay();
}
}
// Track focus time for focus practice
meditationTime++;
if (meditationTime % 60 === 0) {
// Every second
var focusPractice = practices.find(function (practice) {
return practice.id === 'focus';
});
if (focusPractice && !focusPractice.completed) {
focusPractice.count++;
if (focusPractice.count >= focusPractice.target) {
focusPractice.completed = true;
LK.effects.flashObject(practiceTexts[1], 0x83de44, 1000);
// Add soul energy when completing focus practice
soulOrb.addEnergy(30);
}
updatePracticeDisplay();
}
}
// Add soul energy over time with active meditation
if (meditationTime % 180 === 0) {
// Every 3 seconds
soulOrb.addEnergy(1);
// Track soul energy for energy practice
var energyPractice = practices.find(function (practice) {
return practice.id === 'energy';
});
if (energyPractice && !energyPractice.completed) {
energyPractice.count = Math.floor(soulOrb.energy);
if (energyPractice.count >= energyPractice.target) {
energyPractice.completed = true;
LK.effects.flashObject(practiceTexts[2], 0x83de44, 1000);
}
updatePracticeDisplay();
}
}
// Check for meditation level up
if (meditationTime % 600 === 0) {
// Every 10 seconds
meditationLevel++;
levelTxt.setText('Meditation Level: ' + meditationLevel);
// Visual feedback for level up
LK.effects.flashScreen(0x83de44, 500);
}
// Check if all practices are completed
var allPracticesCompleted = practices.every(function (practice) {
return practice.completed;
});
if (allPracticesCompleted && gameActive) {
// Show enlightenment message
var enlightenmentMessage = new Text2('ENLIGHTENMENT ACHIEVED!', {
size: 100,
fill: 0xFFFFFF
});
enlightenmentMessage.anchor.set(0.5, 0.5);
enlightenmentMessage.x = 2048 / 2;
enlightenmentMessage.y = 2732 / 2;
game.addChild(enlightenmentMessage);
// Visual effects for enlightenment
LK.effects.flashScreen(0xFFFFFF, 2000);
// Create new practices with higher requirements
practices = [{
id: 'breathe',
text: 'Complete 10 breathing cycles',
count: 0,
target: 10,
completed: false
}, {
id: 'focus',
text: 'Maintain focus for 120 seconds',
count: 0,
target: 120,
completed: false
}, {
id: 'energy',
text: 'Achieve deeper enlightenment',
count: 0,
target: 200,
completed: false
}];
// Update practice display
for (var i = 0; i < practices.length; i++) {
practiceTexts[i].setText('• ' + practices[i].text + ' (0/' + practices[i].target + ')');
practiceTexts[i].fill = 0xCCCCCC;
}
// Increase soul orb capacity
maxSoulEnergy = 200;
// Remove the enlightenment message after 3 seconds
LK.setTimeout(function () {
enlightenmentMessage.destroy();
updatePracticeDisplay();
}, 3000);
}
};