/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Animal = Container.expand(function () {
var self = Container.call(this);
self.animalGraphic = self.attachAsset('animal', {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = Math.random() > 0.5 ? 1 : -1;
self.speed = Math.random() * 2 + 1;
self.health = 100;
self.hunger = 0;
self.thirsty = 0;
self.state = 'wandering'; // wandering, eating, drinking
self.targetX = null;
self.targetY = null;
self.targetPlant = null;
self.wanderTimer = 0;
if (self.direction === -1) {
self.animalGraphic.scaleX = -1;
}
self.update = function () {
// Natural state changes
self.hunger += 0.1;
self.thirsty += 0.15;
// Cap values
if (self.hunger > 100) {
self.hunger = 100;
}
if (self.thirsty > 100) {
self.thirsty = 100;
}
// Health decreases if hungry or thirsty
if (self.hunger > 80 || self.thirsty > 80) {
self.health -= 0.2;
} else {
// Recover health when not hungry or thirsty
self.health += 0.05;
if (self.health > 100) {
self.health = 100;
}
}
// Decision making
if (self.state === 'wandering') {
// Find food or water if needed
if (self.thirsty > 70 && waterPools.length > 0) {
self.state = 'drinking';
// Find nearest water
var closestDist = Infinity;
var closestWater = null;
for (var i = 0; i < waterPools.length; i++) {
var water = waterPools[i];
var dist = Math.sqrt(Math.pow(self.x - water.x, 2) + Math.pow(self.y - water.y, 2));
if (dist < closestDist) {
closestDist = dist;
closestWater = water;
}
}
if (closestWater) {
self.targetX = closestWater.x + Math.random() * 100 - 50;
self.targetY = closestWater.y - 30;
}
} else if (self.hunger > 60 && plants.length > 0) {
self.state = 'eating';
// Find nearest plant that's mature enough
var closestDist = Infinity;
var closestPlant = null;
for (var j = 0; j < plants.length; j++) {
var plant = plants[j];
if (plant.growthStage > 2) {
// Only eat mature plants
var dist = Math.sqrt(Math.pow(self.x - plant.x, 2) + Math.pow(self.y - plant.y, 2));
if (dist < closestDist) {
closestDist = dist;
closestPlant = plant;
}
}
}
if (closestPlant) {
self.targetPlant = closestPlant;
self.targetX = closestPlant.x;
self.targetY = closestPlant.y;
} else {
// No suitable plants, continue wandering
self.wanderTimer++;
if (self.wanderTimer > 120) {
self.wanderTimer = 0;
self.targetX = Math.random() * 1800 + 100;
}
}
} else {
// Regular wandering
self.wanderTimer++;
if (self.wanderTimer > 180 || self.targetX === null) {
self.wanderTimer = 0;
self.targetX = Math.random() * 1800 + 100;
}
}
// Move towards target if there is one
if (self.targetX !== null) {
var dx = self.targetX - self.x;
if (Math.abs(dx) < 5) {
// Reached target
self.targetX = null;
} else {
// Move towards target
self.direction = dx > 0 ? 1 : -1;
self.animalGraphic.scaleX = self.direction;
self.x += self.direction * self.speed;
}
}
} else if (self.state === 'drinking') {
// Move towards water
if (self.targetX !== null) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 20) {
// Reached water, drink
self.thirsty -= 1;
// Make drinking animation
if (Math.random() < 0.1) {
var splash = new Particle();
splash.x = self.x;
splash.y = self.y + 20;
splash.vx = Math.random() * 2 - 1;
splash.vy = -Math.random() * 2 - 1;
splash.lifespan = 20;
game.addChild(splash);
particles.push(splash);
}
// Done drinking
if (self.thirsty <= 10) {
self.state = 'wandering';
self.targetX = null;
LK.getSound('animalSound').play();
}
} else {
// Move towards water
self.direction = dx > 0 ? 1 : -1;
self.animalGraphic.scaleX = self.direction;
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
} else {
self.state = 'wandering';
}
} else if (self.state === 'eating') {
// Move towards plant
if (self.targetPlant && self.targetPlant.parent) {
var dx = self.targetPlant.x - self.x;
var dy = self.targetPlant.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 30) {
// Reached plant, eat
self.hunger -= 0.5;
self.targetPlant.growthStage -= 0.01;
// Done eating
if (self.hunger <= 10 || self.targetPlant.growthStage <= 1) {
self.state = 'wandering';
self.targetPlant = null;
self.targetX = null;
LK.getSound('animalSound').play();
}
} else {
// Move towards plant
self.direction = dx > 0 ? 1 : -1;
self.animalGraphic.scaleX = self.direction;
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
} else {
self.state = 'wandering';
self.targetPlant = null;
}
}
// Health visual indicator
if (self.health < 50) {
self.animalGraphic.alpha = 0.5 + self.health / 100 * 0.5;
} else {
self.animalGraphic.alpha = 1.0;
}
// Die if health reaches 0
if (self.health <= 0 && self.parent) {
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphic = self.attachAsset('particleSmall', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.vx = 0;
self.vy = 0;
self.lifespan = 100;
self.age = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.age++;
if (self.age > self.lifespan) {
if (self.parent) {
self.destroy();
}
return;
}
particleGraphic.alpha = 1 - self.age / self.lifespan;
};
return self;
});
var Plant = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'grass';
self.growthStage = 0;
self.maxGrowth = 5;
self.growthSpeed = 0;
self.health = 100;
self.waterLevel = 50;
self.sunLevel = 50;
self.buildGraphics = function () {
self.removeChildren();
if (self.type === 'grass') {
var scale = 0.4 + self.growthStage / self.maxGrowth * 0.6;
self.mainGraphic = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: scale,
scaleY: scale
});
} else if (self.type === 'tree') {
var scale = 0.3 + self.growthStage / self.maxGrowth * 0.7;
// Tree trunk
self.trunkGraphic = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: scale,
scaleY: scale
});
// Tree top
self.topGraphic = self.attachAsset('treeTop', {
anchorX: 0.5,
anchorY: 1.0,
y: -self.trunkGraphic.height * scale,
scaleX: scale * 1.2,
scaleY: scale * 1.2
});
} else if (self.type === 'flower') {
var scale = 0.3 + self.growthStage / self.maxGrowth * 0.7;
// Stem
self.stemGraphic = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: scale * 0.5,
scaleY: scale
});
// Flower
self.flowerGraphic = self.attachAsset('flower', {
anchorX: 0.5,
anchorY: 0.5,
y: -self.stemGraphic.height * scale,
scaleX: scale,
scaleY: scale
});
}
};
// Set initial graphics
self.buildGraphics();
self.grow = function () {
if (self.growthStage < self.maxGrowth) {
self.growthStage += 0.1;
self.buildGraphics();
}
};
self.updateHealth = function () {
// Ideal conditions: water 50-70, sun 50-80
if (self.waterLevel < 20 || self.waterLevel > 90) {
self.health -= 0.5;
} else if (self.waterLevel > 40 && self.waterLevel < 80) {
self.health += 0.2;
}
if (self.sunLevel < 30 || self.sunLevel > 90) {
self.health -= 0.5;
} else if (self.sunLevel > 40 && self.sunLevel < 80) {
self.health += 0.2;
}
// Cap health
if (self.health > 100) {
self.health = 100;
}
if (self.health < 0) {
self.health = 0;
}
// Update visual health
var healthColor = 0x33D17A; // Healthy green
if (self.health < 70) {
healthColor = 0xF6D32D;
} // Yellow-ish
if (self.health < 40) {
healthColor = 0xE01B24;
} // Red-ish
// Change tint based on health
if (self.type === 'grass') {
self.mainGraphic.tint = healthColor;
} else if (self.type === 'tree') {
self.topGraphic.tint = healthColor;
} else if (self.type === 'flower') {
self.flowerGraphic.tint = healthColor;
}
// If plant is dead, make it wither
if (self.health <= 0 && self.parent) {
tween(self, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
self.update = function () {
// Natural water loss
self.waterLevel -= 0.1;
// Natural sun effects based on weather
if (currentWeather === 'sunny') {
self.sunLevel += 0.2;
self.waterLevel -= 0.05; // Extra evaporation when sunny
} else if (currentWeather === 'rainy') {
self.sunLevel -= 0.1;
self.waterLevel += 0.3;
} else if (currentWeather === 'windy') {
self.waterLevel -= 0.15; // Wind causes more evaporation
} else if (currentWeather === 'stormy') {
self.sunLevel -= 0.2;
self.waterLevel += 0.5;
}
// Cap water and sun levels
if (self.waterLevel > 100) {
self.waterLevel = 100;
}
if (self.waterLevel < 0) {
self.waterLevel = 0;
}
if (self.sunLevel > 100) {
self.sunLevel = 100;
}
if (self.sunLevel < 0) {
self.sunLevel = 0;
}
// Update health based on conditions
self.updateHealth();
// Calculate growth speed
self.growthSpeed = 0;
// Perfect conditions for growth
if (self.waterLevel > 40 && self.waterLevel < 80 && self.sunLevel > 40 && self.sunLevel < 80) {
self.growthSpeed = 0.01;
}
// Apply growth
if (self.growthSpeed > 0) {
self.grow();
}
};
return self;
});
var Raindrop = Container.expand(function () {
var self = Container.call(this);
var dropGraphic = self.attachAsset('rain', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 5 + 10;
self.alpha = Math.random() * 0.5 + 0.5;
dropGraphic.alpha = self.alpha;
self.update = function () {
self.y += self.speed;
// If raindrop is below ground level
if (self.y > groundLevel) {
// Create splash effect
for (var i = 0; i < 3; i++) {
var splash = new Particle();
splash.x = self.x;
splash.y = groundLevel;
splash.vx = Math.random() * 4 - 2;
splash.vy = -Math.random() * 3 - 2;
splash.lifespan = 20;
game.addChild(splash);
particles.push(splash);
}
if (self.parent) {
self.destroy();
}
}
};
return self;
});
var StormCloud = Container.expand(function () {
var self = Container.call(this);
var cloudBase = self.attachAsset('storm', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 2,
alpha: 0.7
});
// Add some smaller clouds around it for texture
for (var i = 0; i < 5; i++) {
var cloudPuff = self.attachAsset('storm', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 160 - 80,
y: Math.random() * 60 - 30,
scaleX: Math.random() * 1.5 + 0.5,
scaleY: Math.random() * 1.5 + 0.5,
alpha: 0.8
});
}
self.vx = Math.random() * 2 - 1;
self.vy = Math.random() * 0.5 - 0.25;
self.thunderTimer = 0;
self.thunderFrequency = Math.floor(Math.random() * 120) + 180; // 3-5 seconds between thunder
self.lifespan = 600; // 10 seconds life
self.age = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.age++;
// Wrap around edges
if (self.x < -100) {
self.x = 2148;
}
if (self.x > 2148) {
self.x = -100;
}
self.thunderTimer++;
if (self.thunderTimer >= self.thunderFrequency) {
self.thunderTimer = 0;
// Flash the storm cloud
tween(self, {
alpha: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
alpha: 0.7
}, {
duration: 300
});
}
});
// Play thunder sound
LK.getSound('stormSound').play();
// Create rain from the storm
for (var i = 0; i < 8; i++) {
var drop = new Raindrop();
drop.x = self.x + Math.random() * 160 - 80;
drop.y = self.y + 40;
game.addChild(drop);
rain.push(drop);
}
}
if (self.age > self.lifespan) {
if (self.parent) {
tween(self, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
self.destroy();
}
});
}
}
};
return self;
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var sunGraphic = self.attachAsset('sun', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.intensity = 0;
self.targetIntensity = 0;
self.update = function () {
// Smooth transition to target intensity
if (self.intensity < self.targetIntensity) {
self.intensity += 0.01;
} else if (self.intensity > self.targetIntensity) {
self.intensity -= 0.01;
}
// Visual representation of intensity
sunGraphic.alpha = self.intensity;
// Sun rays effect
if (self.intensity > 0.5 && Math.random() < 0.1) {
var ray = new Particle();
ray.x = self.x;
ray.y = self.y;
var angle = Math.random() * Math.PI * 2;
var distance = sunGraphic.width * 0.7;
ray.vx = Math.cos(angle) * 2;
ray.vy = Math.sin(angle) * 2;
ray.x += Math.cos(angle) * distance;
ray.y += Math.sin(angle) * distance;
ray.lifespan = 30;
game.addChild(ray);
particles.push(ray);
}
};
return self;
});
var WaterPool = Container.expand(function () {
var self = Container.call(this);
self.width = 400;
self.height = 200;
var waterGraphic = self.attachAsset('water', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.level = 100; // Water level percentage
self.update = function () {
// Water level changes based on weather
if (currentWeather === 'sunny') {
self.level -= 0.1;
} else if (currentWeather === 'rainy') {
self.level += 0.2;
} else if (currentWeather === 'windy') {
self.level -= 0.05;
} else if (currentWeather === 'stormy') {
self.level += 0.3;
}
// Cap water level
if (self.level > 100) {
self.level = 100;
}
if (self.level < 0) {
self.level = 0;
}
// Visual representation of water level
waterGraphic.scaleY = self.level / 100;
// Create ripples occasionally
if (Math.random() < 0.05 && self.level > 10) {
var ripple = new Particle();
ripple.x = self.x + Math.random() * self.width - self.width / 2;
ripple.y = self.y + Math.random() * self.height / 2 - self.height / 4;
ripple.vx = Math.random() * 0.5 - 0.25;
ripple.vy = Math.random() * 0.5 - 0.25;
ripple.lifespan = 30;
game.addChild(ripple);
particles.push(ripple);
}
};
return self;
});
var WindGust = Container.expand(function () {
var self = Container.call(this);
var gustGraphic = self.attachAsset('wind', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.speed = Math.random() * 8 + 5;
self.lifespan = 120;
self.age = 0;
self.update = function () {
self.x += self.speed;
self.age++;
if (self.age > self.lifespan || self.x > 2048 + gustGraphic.width) {
if (self.parent) {
self.destroy();
}
return;
}
gustGraphic.alpha = 0.3 * (1 - self.age / self.lifespan);
// Create particles along the wind path
if (self.age % 5 === 0) {
var particle = new Particle();
particle.x = self.x;
particle.y = self.y + (Math.random() * gustGraphic.height - gustGraphic.height / 2);
particle.vx = self.speed * 0.8;
particle.vy = Math.random() * 2 - 1;
particle.lifespan = 40;
game.addChild(particle);
particles.push(particle);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Global variables
var groundLevel = 2200;
var currentWeather = 'neutral'; // neutral, sunny, rainy, windy, stormy
var weatherIntensity = 0;
var weatherChangeTimer = 0;
var faceDetected = false;
var lastExpression = 'neutral';
// Arrays to keep track of game objects
var plants = [];
var animals = [];
var rain = [];
var wind = [];
var storms = [];
var particles = [];
var waterPools = [];
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0.0,
x: 2048 / 2,
y: groundLevel
}));
// Create sun
var sun = new Sun();
sun.x = 200;
sun.y = 200;
game.addChild(sun);
// Create water pool
var pool = new WaterPool();
pool.x = 1700;
pool.y = groundLevel - 60;
game.addChild(pool);
waterPools.push(pool);
// UI Elements
var ecosystemTxt = new Text2('Emotion Ecosystem', {
size: 70,
fill: 0xFFFFFF
});
ecosystemTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(ecosystemTxt);
var detectionTxt = new Text2('Looking for your face...', {
size: 40,
fill: 0xFFFFFF
});
detectionTxt.anchor.set(0.5, 0);
detectionTxt.y = 100;
LK.gui.top.addChild(detectionTxt);
var weatherTxt = new Text2('Weather: Neutral', {
size: 40,
fill: 0xFFFFFF
});
weatherTxt.anchor.set(0.5, 0);
weatherTxt.y = 150;
LK.gui.top.addChild(weatherTxt);
var instructionsTxt = new Text2('Smile = Sun\n' + 'Frown = Rain\n' + 'Surprised = Wind\n' + 'Angry = Storm', {
size: 30,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionsTxt);
// Helper functions
function generatePlant(type, x) {
var plant = new Plant(type);
plant.x = x || Math.random() * 1800 + 100;
plant.y = groundLevel;
game.addChild(plant);
plants.push(plant);
return plant;
}
function generateAnimal(x) {
var animal = new Animal();
animal.x = x || Math.random() * 1800 + 100;
animal.y = groundLevel - 20;
game.addChild(animal);
animals.push(animal);
return animal;
}
function createRain() {
var drop = new Raindrop();
drop.x = Math.random() * 2048;
drop.y = -50;
game.addChild(drop);
rain.push(drop);
// Play rain sound occasionally
if (Math.random() < 0.05) {
LK.getSound('rainSound').play();
}
}
function createWind() {
var gust = new WindGust();
gust.x = -100;
gust.y = Math.random() * 1500;
game.addChild(gust);
wind.push(gust);
// Play wind sound occasionally
if (Math.random() < 0.1) {
LK.getSound('windSound').play();
}
}
function createStorm() {
var storm = new StormCloud();
storm.x = Math.random() * 2048;
storm.y = Math.random() * 300 + 100;
game.addChild(storm);
storms.push(storm);
}
function detectFacialExpression() {
// Check if face is detected
if (facekit.mouthCenter.x > 0 || facekit.mouthCenter.y > 0) {
faceDetected = true;
// Check facial expressions
if (facekit.mouthOpen) {
// Calculate how open the mouth is based on distance between upper and lower lip
var mouthOpenness = Math.abs(facekit.upperLip.y - facekit.lowerLip.y) / 100;
if (mouthOpenness > 0.5) {
// Very open mouth - surprised expression
return 'surprised';
}
}
// Check smile (happy expression)
// A smile is detected by curvature of the mouth
// This is a simple approximation - in a real implementation we'd use more robust detection
var mouthWidth = Math.abs(facekit.upperLip.x - facekit.lowerLip.x);
var mouthHeight = Math.abs(facekit.upperLip.y - facekit.lowerLip.y);
if (mouthWidth > 30 && mouthHeight < 20) {
return 'happy';
}
// Check frown (sad expression)
// Simple approximation - in real implementation we'd use more robust detection
if (mouthWidth < 20 && facekit.upperLip.y > facekit.lowerLip.y) {
return 'sad';
}
// Check angry expression
// Simple approximation using eyebrows and mouth
// For this demo, we'll use volume as a proxy for anger
if (facekit.volume > 0.7) {
return 'angry';
}
return 'neutral';
} else {
faceDetected = false;
return 'not detected';
}
}
function updateWeather() {
// Blend from current weather to target weather based on expression
if (lastExpression === 'happy') {
currentWeather = 'sunny';
sun.targetIntensity = 1.0;
} else if (lastExpression === 'sad') {
currentWeather = 'rainy';
sun.targetIntensity = 0.3;
} else if (lastExpression === 'surprised') {
currentWeather = 'windy';
sun.targetIntensity = 0.6;
} else if (lastExpression === 'angry') {
currentWeather = 'stormy';
sun.targetIntensity = 0.2;
} else {
currentWeather = 'neutral';
sun.targetIntensity = 0.5;
}
// Update weather text
weatherTxt.setText('Weather: ' + currentWeather.charAt(0).toUpperCase() + currentWeather.slice(1));
// Spawn weather effects based on current weather
if (currentWeather === 'rainy' && Math.random() < 0.2) {
createRain();
} else if (currentWeather === 'windy' && Math.random() < 0.1) {
createWind();
} else if (currentWeather === 'stormy') {
if (Math.random() < 0.02 && storms.length < 3) {
createStorm();
}
if (Math.random() < 0.1) {
createRain();
}
}
}
// Setup initial ecosystem
function setupInitialEcosystem() {
// Create initial plants
for (var i = 0; i < 5; i++) {
generatePlant('grass', 200 + i * 300);
}
for (var i = 0; i < 3; i++) {
generatePlant('tree', 400 + i * 400);
}
for (var i = 0; i < 4; i++) {
generatePlant('flower', 350 + i * 350);
}
// Create initial animals
for (var i = 0; i < 3; i++) {
generateAnimal(300 + i * 500);
}
// Start background music
LK.playMusic('bgMusic');
}
// Call setup
setupInitialEcosystem();
// Main game loop
game.update = function () {
// Update face detection status
var expression = detectFacialExpression();
if (expression !== 'not detected') {
detectionTxt.setText('Expression: ' + expression.charAt(0).toUpperCase() + expression.slice(1));
lastExpression = expression;
} else {
detectionTxt.setText('Looking for your face...');
}
// Update weather based on expression
updateWeather();
// Update weather timer
weatherChangeTimer++;
if (weatherChangeTimer > 300) {
// Every 5 seconds
weatherChangeTimer = 0;
// Occasionally spawn new plants or animals
if (plants.length < 20 && Math.random() < 0.3) {
var types = ['grass', 'tree', 'flower'];
generatePlant(types[Math.floor(Math.random() * types.length)]);
}
if (animals.length < 8 && Math.random() < 0.2) {
generateAnimal();
}
}
// Update all game objects
sun.update();
// Update plants
for (var i = plants.length - 1; i >= 0; i--) {
if (plants[i].parent) {
plants[i].update();
} else {
plants.splice(i, 1);
}
}
// Update animals
for (var i = animals.length - 1; i >= 0; i--) {
if (animals[i].parent) {
animals[i].update();
} else {
animals.splice(i, 1);
}
}
// Update rain
for (var i = rain.length - 1; i >= 0; i--) {
if (rain[i].parent) {
rain[i].update();
} else {
rain.splice(i, 1);
}
}
// Update wind
for (var i = wind.length - 1; i >= 0; i--) {
if (wind[i].parent) {
wind[i].update();
} else {
wind.splice(i, 1);
}
}
// Update storms
for (var i = storms.length - 1; i >= 0; i--) {
if (storms[i].parent) {
storms[i].update();
} else {
storms.splice(i, 1);
}
}
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i].parent) {
particles[i].update();
} else {
particles.splice(i, 1);
}
}
// Update water pools
for (var i = waterPools.length - 1; i >= 0; i--) {
if (waterPools[i].parent) {
waterPools[i].update();
} else {
waterPools.splice(i, 1);
}
}
// Game over condition - all plants and animals died
if (plants.length === 0 && animals.length === 0) {
LK.showGameOver();
}
// Win condition - flourishing ecosystem (many plants and animals)
if (plants.length > 15 && animals.length > 5) {
// Update score based on ecosystem health
LK.setScore(plants.length + animals.length * 2);
// Check if score is high enough to win
if (LK.getScore() > 30) {
LK.showYouWin();
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,917 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1");
+var facekit = LK.import("@upit/facekit.v1");
+
+/****
+* Classes
+****/
+var Animal = Container.expand(function () {
+ var self = Container.call(this);
+ self.animalGraphic = self.attachAsset('animal', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.direction = Math.random() > 0.5 ? 1 : -1;
+ self.speed = Math.random() * 2 + 1;
+ self.health = 100;
+ self.hunger = 0;
+ self.thirsty = 0;
+ self.state = 'wandering'; // wandering, eating, drinking
+ self.targetX = null;
+ self.targetY = null;
+ self.targetPlant = null;
+ self.wanderTimer = 0;
+ if (self.direction === -1) {
+ self.animalGraphic.scaleX = -1;
+ }
+ self.update = function () {
+ // Natural state changes
+ self.hunger += 0.1;
+ self.thirsty += 0.15;
+ // Cap values
+ if (self.hunger > 100) {
+ self.hunger = 100;
+ }
+ if (self.thirsty > 100) {
+ self.thirsty = 100;
+ }
+ // Health decreases if hungry or thirsty
+ if (self.hunger > 80 || self.thirsty > 80) {
+ self.health -= 0.2;
+ } else {
+ // Recover health when not hungry or thirsty
+ self.health += 0.05;
+ if (self.health > 100) {
+ self.health = 100;
+ }
+ }
+ // Decision making
+ if (self.state === 'wandering') {
+ // Find food or water if needed
+ if (self.thirsty > 70 && waterPools.length > 0) {
+ self.state = 'drinking';
+ // Find nearest water
+ var closestDist = Infinity;
+ var closestWater = null;
+ for (var i = 0; i < waterPools.length; i++) {
+ var water = waterPools[i];
+ var dist = Math.sqrt(Math.pow(self.x - water.x, 2) + Math.pow(self.y - water.y, 2));
+ if (dist < closestDist) {
+ closestDist = dist;
+ closestWater = water;
+ }
+ }
+ if (closestWater) {
+ self.targetX = closestWater.x + Math.random() * 100 - 50;
+ self.targetY = closestWater.y - 30;
+ }
+ } else if (self.hunger > 60 && plants.length > 0) {
+ self.state = 'eating';
+ // Find nearest plant that's mature enough
+ var closestDist = Infinity;
+ var closestPlant = null;
+ for (var j = 0; j < plants.length; j++) {
+ var plant = plants[j];
+ if (plant.growthStage > 2) {
+ // Only eat mature plants
+ var dist = Math.sqrt(Math.pow(self.x - plant.x, 2) + Math.pow(self.y - plant.y, 2));
+ if (dist < closestDist) {
+ closestDist = dist;
+ closestPlant = plant;
+ }
+ }
+ }
+ if (closestPlant) {
+ self.targetPlant = closestPlant;
+ self.targetX = closestPlant.x;
+ self.targetY = closestPlant.y;
+ } else {
+ // No suitable plants, continue wandering
+ self.wanderTimer++;
+ if (self.wanderTimer > 120) {
+ self.wanderTimer = 0;
+ self.targetX = Math.random() * 1800 + 100;
+ }
+ }
+ } else {
+ // Regular wandering
+ self.wanderTimer++;
+ if (self.wanderTimer > 180 || self.targetX === null) {
+ self.wanderTimer = 0;
+ self.targetX = Math.random() * 1800 + 100;
+ }
+ }
+ // Move towards target if there is one
+ if (self.targetX !== null) {
+ var dx = self.targetX - self.x;
+ if (Math.abs(dx) < 5) {
+ // Reached target
+ self.targetX = null;
+ } else {
+ // Move towards target
+ self.direction = dx > 0 ? 1 : -1;
+ self.animalGraphic.scaleX = self.direction;
+ self.x += self.direction * self.speed;
+ }
+ }
+ } else if (self.state === 'drinking') {
+ // Move towards water
+ if (self.targetX !== null) {
+ var dx = self.targetX - self.x;
+ var dy = self.targetY - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 20) {
+ // Reached water, drink
+ self.thirsty -= 1;
+ // Make drinking animation
+ if (Math.random() < 0.1) {
+ var splash = new Particle();
+ splash.x = self.x;
+ splash.y = self.y + 20;
+ splash.vx = Math.random() * 2 - 1;
+ splash.vy = -Math.random() * 2 - 1;
+ splash.lifespan = 20;
+ game.addChild(splash);
+ particles.push(splash);
+ }
+ // Done drinking
+ if (self.thirsty <= 10) {
+ self.state = 'wandering';
+ self.targetX = null;
+ LK.getSound('animalSound').play();
+ }
+ } else {
+ // Move towards water
+ self.direction = dx > 0 ? 1 : -1;
+ self.animalGraphic.scaleX = self.direction;
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ }
+ } else {
+ self.state = 'wandering';
+ }
+ } else if (self.state === 'eating') {
+ // Move towards plant
+ if (self.targetPlant && self.targetPlant.parent) {
+ var dx = self.targetPlant.x - self.x;
+ var dy = self.targetPlant.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 30) {
+ // Reached plant, eat
+ self.hunger -= 0.5;
+ self.targetPlant.growthStage -= 0.01;
+ // Done eating
+ if (self.hunger <= 10 || self.targetPlant.growthStage <= 1) {
+ self.state = 'wandering';
+ self.targetPlant = null;
+ self.targetX = null;
+ LK.getSound('animalSound').play();
+ }
+ } else {
+ // Move towards plant
+ self.direction = dx > 0 ? 1 : -1;
+ self.animalGraphic.scaleX = self.direction;
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ }
+ } else {
+ self.state = 'wandering';
+ self.targetPlant = null;
+ }
+ }
+ // Health visual indicator
+ if (self.health < 50) {
+ self.animalGraphic.alpha = 0.5 + self.health / 100 * 0.5;
+ } else {
+ self.animalGraphic.alpha = 1.0;
+ }
+ // Die if health reaches 0
+ if (self.health <= 0 && self.parent) {
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ }
+ };
+ return self;
+});
+var Particle = Container.expand(function () {
+ var self = Container.call(this);
+ var particleGraphic = self.attachAsset('particleSmall', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.8
+ });
+ self.vx = 0;
+ self.vy = 0;
+ self.lifespan = 100;
+ self.age = 0;
+ self.update = function () {
+ self.x += self.vx;
+ self.y += self.vy;
+ self.age++;
+ if (self.age > self.lifespan) {
+ if (self.parent) {
+ self.destroy();
+ }
+ return;
+ }
+ particleGraphic.alpha = 1 - self.age / self.lifespan;
+ };
+ return self;
+});
+var Plant = Container.expand(function (type) {
+ var self = Container.call(this);
+ self.type = type || 'grass';
+ self.growthStage = 0;
+ self.maxGrowth = 5;
+ self.growthSpeed = 0;
+ self.health = 100;
+ self.waterLevel = 50;
+ self.sunLevel = 50;
+ self.buildGraphics = function () {
+ self.removeChildren();
+ if (self.type === 'grass') {
+ var scale = 0.4 + self.growthStage / self.maxGrowth * 0.6;
+ self.mainGraphic = self.attachAsset('grass', {
+ anchorX: 0.5,
+ anchorY: 1.0,
+ scaleX: scale,
+ scaleY: scale
+ });
+ } else if (self.type === 'tree') {
+ var scale = 0.3 + self.growthStage / self.maxGrowth * 0.7;
+ // Tree trunk
+ self.trunkGraphic = self.attachAsset('tree', {
+ anchorX: 0.5,
+ anchorY: 1.0,
+ scaleX: scale,
+ scaleY: scale
+ });
+ // Tree top
+ self.topGraphic = self.attachAsset('treeTop', {
+ anchorX: 0.5,
+ anchorY: 1.0,
+ y: -self.trunkGraphic.height * scale,
+ scaleX: scale * 1.2,
+ scaleY: scale * 1.2
+ });
+ } else if (self.type === 'flower') {
+ var scale = 0.3 + self.growthStage / self.maxGrowth * 0.7;
+ // Stem
+ self.stemGraphic = self.attachAsset('grass', {
+ anchorX: 0.5,
+ anchorY: 1.0,
+ scaleX: scale * 0.5,
+ scaleY: scale
+ });
+ // Flower
+ self.flowerGraphic = self.attachAsset('flower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: -self.stemGraphic.height * scale,
+ scaleX: scale,
+ scaleY: scale
+ });
+ }
+ };
+ // Set initial graphics
+ self.buildGraphics();
+ self.grow = function () {
+ if (self.growthStage < self.maxGrowth) {
+ self.growthStage += 0.1;
+ self.buildGraphics();
+ }
+ };
+ self.updateHealth = function () {
+ // Ideal conditions: water 50-70, sun 50-80
+ if (self.waterLevel < 20 || self.waterLevel > 90) {
+ self.health -= 0.5;
+ } else if (self.waterLevel > 40 && self.waterLevel < 80) {
+ self.health += 0.2;
+ }
+ if (self.sunLevel < 30 || self.sunLevel > 90) {
+ self.health -= 0.5;
+ } else if (self.sunLevel > 40 && self.sunLevel < 80) {
+ self.health += 0.2;
+ }
+ // Cap health
+ if (self.health > 100) {
+ self.health = 100;
+ }
+ if (self.health < 0) {
+ self.health = 0;
+ }
+ // Update visual health
+ var healthColor = 0x33D17A; // Healthy green
+ if (self.health < 70) {
+ healthColor = 0xF6D32D;
+ } // Yellow-ish
+ if (self.health < 40) {
+ healthColor = 0xE01B24;
+ } // Red-ish
+ // Change tint based on health
+ if (self.type === 'grass') {
+ self.mainGraphic.tint = healthColor;
+ } else if (self.type === 'tree') {
+ self.topGraphic.tint = healthColor;
+ } else if (self.type === 'flower') {
+ self.flowerGraphic.tint = healthColor;
+ }
+ // If plant is dead, make it wither
+ if (self.health <= 0 && self.parent) {
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 2000,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ }
+ };
+ self.update = function () {
+ // Natural water loss
+ self.waterLevel -= 0.1;
+ // Natural sun effects based on weather
+ if (currentWeather === 'sunny') {
+ self.sunLevel += 0.2;
+ self.waterLevel -= 0.05; // Extra evaporation when sunny
+ } else if (currentWeather === 'rainy') {
+ self.sunLevel -= 0.1;
+ self.waterLevel += 0.3;
+ } else if (currentWeather === 'windy') {
+ self.waterLevel -= 0.15; // Wind causes more evaporation
+ } else if (currentWeather === 'stormy') {
+ self.sunLevel -= 0.2;
+ self.waterLevel += 0.5;
+ }
+ // Cap water and sun levels
+ if (self.waterLevel > 100) {
+ self.waterLevel = 100;
+ }
+ if (self.waterLevel < 0) {
+ self.waterLevel = 0;
+ }
+ if (self.sunLevel > 100) {
+ self.sunLevel = 100;
+ }
+ if (self.sunLevel < 0) {
+ self.sunLevel = 0;
+ }
+ // Update health based on conditions
+ self.updateHealth();
+ // Calculate growth speed
+ self.growthSpeed = 0;
+ // Perfect conditions for growth
+ if (self.waterLevel > 40 && self.waterLevel < 80 && self.sunLevel > 40 && self.sunLevel < 80) {
+ self.growthSpeed = 0.01;
+ }
+ // Apply growth
+ if (self.growthSpeed > 0) {
+ self.grow();
+ }
+ };
+ return self;
+});
+var Raindrop = Container.expand(function () {
+ var self = Container.call(this);
+ var dropGraphic = self.attachAsset('rain', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = Math.random() * 5 + 10;
+ self.alpha = Math.random() * 0.5 + 0.5;
+ dropGraphic.alpha = self.alpha;
+ self.update = function () {
+ self.y += self.speed;
+ // If raindrop is below ground level
+ if (self.y > groundLevel) {
+ // Create splash effect
+ for (var i = 0; i < 3; i++) {
+ var splash = new Particle();
+ splash.x = self.x;
+ splash.y = groundLevel;
+ splash.vx = Math.random() * 4 - 2;
+ splash.vy = -Math.random() * 3 - 2;
+ splash.lifespan = 20;
+ game.addChild(splash);
+ particles.push(splash);
+ }
+ if (self.parent) {
+ self.destroy();
+ }
+ }
+ };
+ return self;
+});
+var StormCloud = Container.expand(function () {
+ var self = Container.call(this);
+ var cloudBase = self.attachAsset('storm', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 3,
+ scaleY: 2,
+ alpha: 0.7
+ });
+ // Add some smaller clouds around it for texture
+ for (var i = 0; i < 5; i++) {
+ var cloudPuff = self.attachAsset('storm', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: Math.random() * 160 - 80,
+ y: Math.random() * 60 - 30,
+ scaleX: Math.random() * 1.5 + 0.5,
+ scaleY: Math.random() * 1.5 + 0.5,
+ alpha: 0.8
+ });
+ }
+ self.vx = Math.random() * 2 - 1;
+ self.vy = Math.random() * 0.5 - 0.25;
+ self.thunderTimer = 0;
+ self.thunderFrequency = Math.floor(Math.random() * 120) + 180; // 3-5 seconds between thunder
+ self.lifespan = 600; // 10 seconds life
+ self.age = 0;
+ self.update = function () {
+ self.x += self.vx;
+ self.y += self.vy;
+ self.age++;
+ // Wrap around edges
+ if (self.x < -100) {
+ self.x = 2148;
+ }
+ if (self.x > 2148) {
+ self.x = -100;
+ }
+ self.thunderTimer++;
+ if (self.thunderTimer >= self.thunderFrequency) {
+ self.thunderTimer = 0;
+ // Flash the storm cloud
+ tween(self, {
+ alpha: 0.9
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ tween(self, {
+ alpha: 0.7
+ }, {
+ duration: 300
+ });
+ }
+ });
+ // Play thunder sound
+ LK.getSound('stormSound').play();
+ // Create rain from the storm
+ for (var i = 0; i < 8; i++) {
+ var drop = new Raindrop();
+ drop.x = self.x + Math.random() * 160 - 80;
+ drop.y = self.y + 40;
+ game.addChild(drop);
+ rain.push(drop);
+ }
+ }
+ if (self.age > self.lifespan) {
+ if (self.parent) {
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 500,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ }
+ }
+ };
+ return self;
+});
+var Sun = Container.expand(function () {
+ var self = Container.call(this);
+ var sunGraphic = self.attachAsset('sun', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 1.5,
+ scaleY: 1.5
+ });
+ self.intensity = 0;
+ self.targetIntensity = 0;
+ self.update = function () {
+ // Smooth transition to target intensity
+ if (self.intensity < self.targetIntensity) {
+ self.intensity += 0.01;
+ } else if (self.intensity > self.targetIntensity) {
+ self.intensity -= 0.01;
+ }
+ // Visual representation of intensity
+ sunGraphic.alpha = self.intensity;
+ // Sun rays effect
+ if (self.intensity > 0.5 && Math.random() < 0.1) {
+ var ray = new Particle();
+ ray.x = self.x;
+ ray.y = self.y;
+ var angle = Math.random() * Math.PI * 2;
+ var distance = sunGraphic.width * 0.7;
+ ray.vx = Math.cos(angle) * 2;
+ ray.vy = Math.sin(angle) * 2;
+ ray.x += Math.cos(angle) * distance;
+ ray.y += Math.sin(angle) * distance;
+ ray.lifespan = 30;
+ game.addChild(ray);
+ particles.push(ray);
+ }
+ };
+ return self;
+});
+var WaterPool = Container.expand(function () {
+ var self = Container.call(this);
+ self.width = 400;
+ self.height = 200;
+ var waterGraphic = self.attachAsset('water', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ });
+ self.level = 100; // Water level percentage
+ self.update = function () {
+ // Water level changes based on weather
+ if (currentWeather === 'sunny') {
+ self.level -= 0.1;
+ } else if (currentWeather === 'rainy') {
+ self.level += 0.2;
+ } else if (currentWeather === 'windy') {
+ self.level -= 0.05;
+ } else if (currentWeather === 'stormy') {
+ self.level += 0.3;
+ }
+ // Cap water level
+ if (self.level > 100) {
+ self.level = 100;
+ }
+ if (self.level < 0) {
+ self.level = 0;
+ }
+ // Visual representation of water level
+ waterGraphic.scaleY = self.level / 100;
+ // Create ripples occasionally
+ if (Math.random() < 0.05 && self.level > 10) {
+ var ripple = new Particle();
+ ripple.x = self.x + Math.random() * self.width - self.width / 2;
+ ripple.y = self.y + Math.random() * self.height / 2 - self.height / 4;
+ ripple.vx = Math.random() * 0.5 - 0.25;
+ ripple.vy = Math.random() * 0.5 - 0.25;
+ ripple.lifespan = 30;
+ game.addChild(ripple);
+ particles.push(ripple);
+ }
+ };
+ return self;
+});
+var WindGust = Container.expand(function () {
+ var self = Container.call(this);
+ var gustGraphic = self.attachAsset('wind', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.3
+ });
+ self.speed = Math.random() * 8 + 5;
+ self.lifespan = 120;
+ self.age = 0;
+ self.update = function () {
+ self.x += self.speed;
+ self.age++;
+ if (self.age > self.lifespan || self.x > 2048 + gustGraphic.width) {
+ if (self.parent) {
+ self.destroy();
+ }
+ return;
+ }
+ gustGraphic.alpha = 0.3 * (1 - self.age / self.lifespan);
+ // Create particles along the wind path
+ if (self.age % 5 === 0) {
+ var particle = new Particle();
+ particle.x = self.x;
+ particle.y = self.y + (Math.random() * gustGraphic.height - gustGraphic.height / 2);
+ particle.vx = self.speed * 0.8;
+ particle.vy = Math.random() * 2 - 1;
+ particle.lifespan = 40;
+ game.addChild(particle);
+ particles.push(particle);
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x87CEEB // Sky blue background
+});
+
+/****
+* Game Code
+****/
+// Global variables
+var groundLevel = 2200;
+var currentWeather = 'neutral'; // neutral, sunny, rainy, windy, stormy
+var weatherIntensity = 0;
+var weatherChangeTimer = 0;
+var faceDetected = false;
+var lastExpression = 'neutral';
+// Arrays to keep track of game objects
+var plants = [];
+var animals = [];
+var rain = [];
+var wind = [];
+var storms = [];
+var particles = [];
+var waterPools = [];
+// Create ground
+var ground = game.addChild(LK.getAsset('ground', {
+ anchorX: 0.5,
+ anchorY: 0.0,
+ x: 2048 / 2,
+ y: groundLevel
+}));
+// Create sun
+var sun = new Sun();
+sun.x = 200;
+sun.y = 200;
+game.addChild(sun);
+// Create water pool
+var pool = new WaterPool();
+pool.x = 1700;
+pool.y = groundLevel - 60;
+game.addChild(pool);
+waterPools.push(pool);
+// UI Elements
+var ecosystemTxt = new Text2('Emotion Ecosystem', {
+ size: 70,
+ fill: 0xFFFFFF
+});
+ecosystemTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(ecosystemTxt);
+var detectionTxt = new Text2('Looking for your face...', {
+ size: 40,
+ fill: 0xFFFFFF
+});
+detectionTxt.anchor.set(0.5, 0);
+detectionTxt.y = 100;
+LK.gui.top.addChild(detectionTxt);
+var weatherTxt = new Text2('Weather: Neutral', {
+ size: 40,
+ fill: 0xFFFFFF
+});
+weatherTxt.anchor.set(0.5, 0);
+weatherTxt.y = 150;
+LK.gui.top.addChild(weatherTxt);
+var instructionsTxt = new Text2('Smile = Sun\n' + 'Frown = Rain\n' + 'Surprised = Wind\n' + 'Angry = Storm', {
+ size: 30,
+ fill: 0xFFFFFF
+});
+instructionsTxt.anchor.set(0.5, 1);
+LK.gui.bottom.addChild(instructionsTxt);
+// Helper functions
+function generatePlant(type, x) {
+ var plant = new Plant(type);
+ plant.x = x || Math.random() * 1800 + 100;
+ plant.y = groundLevel;
+ game.addChild(plant);
+ plants.push(plant);
+ return plant;
+}
+function generateAnimal(x) {
+ var animal = new Animal();
+ animal.x = x || Math.random() * 1800 + 100;
+ animal.y = groundLevel - 20;
+ game.addChild(animal);
+ animals.push(animal);
+ return animal;
+}
+function createRain() {
+ var drop = new Raindrop();
+ drop.x = Math.random() * 2048;
+ drop.y = -50;
+ game.addChild(drop);
+ rain.push(drop);
+ // Play rain sound occasionally
+ if (Math.random() < 0.05) {
+ LK.getSound('rainSound').play();
+ }
+}
+function createWind() {
+ var gust = new WindGust();
+ gust.x = -100;
+ gust.y = Math.random() * 1500;
+ game.addChild(gust);
+ wind.push(gust);
+ // Play wind sound occasionally
+ if (Math.random() < 0.1) {
+ LK.getSound('windSound').play();
+ }
+}
+function createStorm() {
+ var storm = new StormCloud();
+ storm.x = Math.random() * 2048;
+ storm.y = Math.random() * 300 + 100;
+ game.addChild(storm);
+ storms.push(storm);
+}
+function detectFacialExpression() {
+ // Check if face is detected
+ if (facekit.mouthCenter.x > 0 || facekit.mouthCenter.y > 0) {
+ faceDetected = true;
+ // Check facial expressions
+ if (facekit.mouthOpen) {
+ // Calculate how open the mouth is based on distance between upper and lower lip
+ var mouthOpenness = Math.abs(facekit.upperLip.y - facekit.lowerLip.y) / 100;
+ if (mouthOpenness > 0.5) {
+ // Very open mouth - surprised expression
+ return 'surprised';
+ }
+ }
+ // Check smile (happy expression)
+ // A smile is detected by curvature of the mouth
+ // This is a simple approximation - in a real implementation we'd use more robust detection
+ var mouthWidth = Math.abs(facekit.upperLip.x - facekit.lowerLip.x);
+ var mouthHeight = Math.abs(facekit.upperLip.y - facekit.lowerLip.y);
+ if (mouthWidth > 30 && mouthHeight < 20) {
+ return 'happy';
+ }
+ // Check frown (sad expression)
+ // Simple approximation - in real implementation we'd use more robust detection
+ if (mouthWidth < 20 && facekit.upperLip.y > facekit.lowerLip.y) {
+ return 'sad';
+ }
+ // Check angry expression
+ // Simple approximation using eyebrows and mouth
+ // For this demo, we'll use volume as a proxy for anger
+ if (facekit.volume > 0.7) {
+ return 'angry';
+ }
+ return 'neutral';
+ } else {
+ faceDetected = false;
+ return 'not detected';
+ }
+}
+function updateWeather() {
+ // Blend from current weather to target weather based on expression
+ if (lastExpression === 'happy') {
+ currentWeather = 'sunny';
+ sun.targetIntensity = 1.0;
+ } else if (lastExpression === 'sad') {
+ currentWeather = 'rainy';
+ sun.targetIntensity = 0.3;
+ } else if (lastExpression === 'surprised') {
+ currentWeather = 'windy';
+ sun.targetIntensity = 0.6;
+ } else if (lastExpression === 'angry') {
+ currentWeather = 'stormy';
+ sun.targetIntensity = 0.2;
+ } else {
+ currentWeather = 'neutral';
+ sun.targetIntensity = 0.5;
+ }
+ // Update weather text
+ weatherTxt.setText('Weather: ' + currentWeather.charAt(0).toUpperCase() + currentWeather.slice(1));
+ // Spawn weather effects based on current weather
+ if (currentWeather === 'rainy' && Math.random() < 0.2) {
+ createRain();
+ } else if (currentWeather === 'windy' && Math.random() < 0.1) {
+ createWind();
+ } else if (currentWeather === 'stormy') {
+ if (Math.random() < 0.02 && storms.length < 3) {
+ createStorm();
+ }
+ if (Math.random() < 0.1) {
+ createRain();
+ }
+ }
+}
+// Setup initial ecosystem
+function setupInitialEcosystem() {
+ // Create initial plants
+ for (var i = 0; i < 5; i++) {
+ generatePlant('grass', 200 + i * 300);
+ }
+ for (var i = 0; i < 3; i++) {
+ generatePlant('tree', 400 + i * 400);
+ }
+ for (var i = 0; i < 4; i++) {
+ generatePlant('flower', 350 + i * 350);
+ }
+ // Create initial animals
+ for (var i = 0; i < 3; i++) {
+ generateAnimal(300 + i * 500);
+ }
+ // Start background music
+ LK.playMusic('bgMusic');
+}
+// Call setup
+setupInitialEcosystem();
+// Main game loop
+game.update = function () {
+ // Update face detection status
+ var expression = detectFacialExpression();
+ if (expression !== 'not detected') {
+ detectionTxt.setText('Expression: ' + expression.charAt(0).toUpperCase() + expression.slice(1));
+ lastExpression = expression;
+ } else {
+ detectionTxt.setText('Looking for your face...');
+ }
+ // Update weather based on expression
+ updateWeather();
+ // Update weather timer
+ weatherChangeTimer++;
+ if (weatherChangeTimer > 300) {
+ // Every 5 seconds
+ weatherChangeTimer = 0;
+ // Occasionally spawn new plants or animals
+ if (plants.length < 20 && Math.random() < 0.3) {
+ var types = ['grass', 'tree', 'flower'];
+ generatePlant(types[Math.floor(Math.random() * types.length)]);
+ }
+ if (animals.length < 8 && Math.random() < 0.2) {
+ generateAnimal();
+ }
+ }
+ // Update all game objects
+ sun.update();
+ // Update plants
+ for (var i = plants.length - 1; i >= 0; i--) {
+ if (plants[i].parent) {
+ plants[i].update();
+ } else {
+ plants.splice(i, 1);
+ }
+ }
+ // Update animals
+ for (var i = animals.length - 1; i >= 0; i--) {
+ if (animals[i].parent) {
+ animals[i].update();
+ } else {
+ animals.splice(i, 1);
+ }
+ }
+ // Update rain
+ for (var i = rain.length - 1; i >= 0; i--) {
+ if (rain[i].parent) {
+ rain[i].update();
+ } else {
+ rain.splice(i, 1);
+ }
+ }
+ // Update wind
+ for (var i = wind.length - 1; i >= 0; i--) {
+ if (wind[i].parent) {
+ wind[i].update();
+ } else {
+ wind.splice(i, 1);
+ }
+ }
+ // Update storms
+ for (var i = storms.length - 1; i >= 0; i--) {
+ if (storms[i].parent) {
+ storms[i].update();
+ } else {
+ storms.splice(i, 1);
+ }
+ }
+ // Update particles
+ for (var i = particles.length - 1; i >= 0; i--) {
+ if (particles[i].parent) {
+ particles[i].update();
+ } else {
+ particles.splice(i, 1);
+ }
+ }
+ // Update water pools
+ for (var i = waterPools.length - 1; i >= 0; i--) {
+ if (waterPools[i].parent) {
+ waterPools[i].update();
+ } else {
+ waterPools.splice(i, 1);
+ }
+ }
+ // Game over condition - all plants and animals died
+ if (plants.length === 0 && animals.length === 0) {
+ LK.showGameOver();
+ }
+ // Win condition - flourishing ecosystem (many plants and animals)
+ if (plants.length > 15 && animals.length > 5) {
+ // Update score based on ecosystem health
+ LK.setScore(plants.length + animals.length * 2);
+ // Check if score is high enough to win
+ if (LK.getScore() > 30) {
+ LK.showYouWin();
+ }
+ }
+};
\ No newline at end of file