/****
* 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();
}
}
}; /****
* 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();
}
}
};