/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BlueSpirit = Container.expand(function () { var self = Container.call(this); var spiritGraphics = self.attachAsset('blueSpirit', { anchorX: 0.5, anchorY: 0.5 }); self.waitTime = 0; self.maxWaitTime = 600; // 10 seconds at 60fps (default for both levels) self.isDark = false; self.speed = 1; self.update = function () { self.waitTime++; // Set maxWaitTime based on current level if (currentLevel === 'level1') { self.maxWaitTime = 600; // 10 seconds at 60fps } else if (currentLevel === 'level2') { self.maxWaitTime = 600; // 10 seconds at 60fps } if (!self.isDark && self.waitTime >= self.maxWaitTime) { self.turnDark(); } if (self.isDark) { self.y += self.speed; } }; self.turnDark = function () { self.isDark = true; spiritGraphics.tint = 0x2a0845; tween(spiritGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); }; return self; }); var DarkSpirit = Container.expand(function () { var self = Container.call(this); var darkGraphics = self.attachAsset('darkSpirit', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; self.targetPlayer = null; self.update = function () { if (self.targetPlayer) { var dx = self.targetPlayer.x - self.x; var dy = self.targetPlayer.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Change direction based on position relative to screen middle var screenMiddle = 1024; if (self.x < screenMiddle) { // Dark spirit is on left side, face right towards middle self.scaleX = 1; } else { // Dark spirit is on right side, face left towards middle self.scaleX = -1; } } }; return self; }); var HealthBar = Container.expand(function () { var self = Container.call(this); // Background bar self.bgBar = self.attachAsset('healthBarBg', { anchorX: 0, anchorY: 0 }); // Health bar self.healthBar = self.attachAsset('healthBarFill', { anchorX: 0, anchorY: 0 }); self.maxWidth = 300; self.updateHealth = function (healthPercent) { self.healthBar.scaleX = healthPercent / 100; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.tokens = 0; self.health = 100; self.maxHealth = 100; self.collectToken = function () { self.tokens++; }; self.deliverTokens = function () { var delivered = self.tokens; self.tokens = 0; return delivered; }; self.takeDamage = function () { var damage = Math.random() * 7 + 3; // Random between 3-10 self.health -= damage; self.health = Math.max(0, self.health); // Ensure health doesn't go below 0 LK.effects.flashObject(self, 0xff0000, 500); return self.health <= 0; }; return self; }); var PollutedSpirit = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit1', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit2 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit2', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit3 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit3', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit4 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit4', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit5 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit5', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit6 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit6', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit7 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit7', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PollutedSpirit8 = Container.expand(function () { var self = Container.call(this); var pollutedGraphics = self.attachAsset('pollutedspirit8', { anchorX: 0.5, anchorY: 0.5 }); self.tokensNeeded = 3; self.isCleansed = false; self.cleanse = function () { self.isCleansed = true; tween(pollutedGraphics, { tint: 0x98fb98 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var RiverSpirit = Container.expand(function () { var self = Container.call(this); var riverGraphics = self.attachAsset('riverSpirit', { anchorX: 0.5, anchorY: 0.5 }); self.bonusValue = 20; self.speed = 0.5; self.update = function () { self.x += self.speed; self.rotation += 0.01; }; return self; }); var ShadowSpirit = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('shadowSpirit', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.targetPlayer = null; self.update = function () { if (self.targetPlayer) { var dx = self.targetPlayer.x - self.x; var dy = self.targetPlayer.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Change direction based on position relative to screen middle var screenMiddle = 1024; if (self.x < screenMiddle) { // Shadow is on left side, face right towards middle self.scaleX = 1; } else { // Shadow is on right side, face left towards middle self.scaleX = -1; } } }; return self; }); var Token = Container.expand(function () { var self = Container.call(this); var tokenGraphics = self.attachAsset('token', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.update = function () { if (!self.collected) { self.rotation += 0.02; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ var gameStarted = false; var introBackground = null; var startButton = null; var currentLevel = 'level1'; var gameBackground = null; // Declare game variables in global scope var player, tokens, blueSpirits, shadowSpirits, pollutedSpirits, riverSpirit, darkSpirits; var spawnTimer, spawnInterval, shadowSpawnTimer, shadowSpawnInterval; var scoreTxt, tokenTxt, healthBar; var brushSprite = null; var showerAreas = [], pollutedSpawnTimer, pollutedSpawnInterval; var maxShadowSpirits = 2; // Level 1: 2, Level 2: 4 var shadowRespawnTimer = 0; var shadowRespawnInterval = 180; // 3 seconds to respawn var lastMovementSoundTime = 0; var movementSoundCooldown = 1000; // Milliseconds between sound plays // Show intro screen function showIntroScreen() { introBackground = game.addChild(LK.getAsset('introbackground', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); startButton = game.addChild(LK.getAsset('startbutton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2400 })); // Only play intro music if game hasn't started and we're actually showing intro screen if (!gameStarted && introBackground && startButton) { LK.playMusic('intromusic'); } } // Start the actual game function startGame() { gameStarted = true; if (introBackground) { introBackground.destroy(); introBackground = null; } if (startButton) { startButton.destroy(); startButton = null; } LK.stopMusic(); LK.playMusic('level1music'); initializeGame(); } // Spawn functions in global scope function spawnToken() { var token = new Token(); token.x = Math.random() * 1848 + 100; token.y = Math.random() * 2332 + 300; tokens.push(token); game.addChild(token); } function spawnSpirit() { var spirit = new BlueSpirit(); spirit.x = Math.random() * 1848 + 100; spirit.y = 300; blueSpirits.push(spirit); game.addChild(spirit); } function spawnShadowSpirit() { // Only spawn if under the limit if (shadowSpirits.length < maxShadowSpirits) { var shadow = new ShadowSpirit(); shadow.x = Math.random() * 1848 + 100; shadow.y = Math.random() * 400 + 300; shadow.targetPlayer = player; shadowSpirits.push(shadow); game.addChild(shadow); LK.getSound('Shadowspiritsound').play(); } } function spawnPollutedSpirit() { // Find an empty shower area var emptyAreas = []; for (var i = 0; i < showerAreas.length; i++) { if (!showerAreas[i].occupied) { emptyAreas.push(showerAreas[i]); } } // Only spawn if there's an empty area if (emptyAreas.length > 0) { if (currentLevel === 'level4') { // Level 4: Spawn only PollutedSpirit6, PollutedSpirit7, PollutedSpirit8 if (emptyAreas.length >= 3) { var pollutedTypes = [PollutedSpirit6, PollutedSpirit7, PollutedSpirit8]; for (var t = 0; t < pollutedTypes.length; t++) { var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var polluted = new pollutedTypes[t](); polluted.x = selectedArea.x; polluted.y = selectedArea.y; polluted.assignedShowerArea = selectedArea; selectedArea.occupied = true; pollutedSpirits.push(polluted); game.addChild(polluted); // Spawn DarkSpirit behind polluted spirit var dark = new DarkSpirit(); dark.x = polluted.x - 50; dark.y = polluted.y + 30; dark.targetPlayer = player; darkSpirits.push(dark); game.addChild(dark); LK.getSound('darkspiritsound').play(); // Remove the used area from available areas emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1); } } else { // Spawn random types based on available areas var pollutedTypes = [PollutedSpirit6, PollutedSpirit7, PollutedSpirit8]; for (var a = 0; a < Math.min(emptyAreas.length, 3); a++) { var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var randomType = pollutedTypes[Math.floor(Math.random() * pollutedTypes.length)]; var polluted = new randomType(); polluted.x = selectedArea.x; polluted.y = selectedArea.y; polluted.assignedShowerArea = selectedArea; selectedArea.occupied = true; pollutedSpirits.push(polluted); game.addChild(polluted); // Spawn DarkSpirit behind polluted spirit var dark = new DarkSpirit(); dark.x = polluted.x - 50; dark.y = polluted.y + 30; dark.targetPlayer = player; darkSpirits.push(dark); game.addChild(dark); LK.getSound('darkspiritsound').play(); // Remove the used area from available areas emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1); } } } else if (currentLevel === 'level3') { // Level 3: Spawn all 5 types if we have at least 5 empty areas if (emptyAreas.length >= 5) { var pollutedTypes = [PollutedSpirit, PollutedSpirit2, PollutedSpirit3, PollutedSpirit4, PollutedSpirit5]; for (var t = 0; t < pollutedTypes.length; t++) { var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var polluted = new pollutedTypes[t](); polluted.x = selectedArea.x; polluted.y = selectedArea.y; polluted.assignedShowerArea = selectedArea; selectedArea.occupied = true; pollutedSpirits.push(polluted); game.addChild(polluted); // Spawn DarkSpirit behind polluted spirit var dark = new DarkSpirit(); dark.x = polluted.x - 50; dark.y = polluted.y + 30; dark.targetPlayer = player; darkSpirits.push(dark); game.addChild(dark); LK.getSound('darkspiritsound').play(); // Remove the used area from available areas emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1); } } else { // Spawn random types based on available areas var pollutedTypes = [PollutedSpirit, PollutedSpirit2, PollutedSpirit3, PollutedSpirit4, PollutedSpirit5]; for (var a = 0; a < Math.min(emptyAreas.length, 3); a++) { var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var randomType = pollutedTypes[Math.floor(Math.random() * pollutedTypes.length)]; var polluted = new randomType(); polluted.x = selectedArea.x; polluted.y = selectedArea.y; polluted.assignedShowerArea = selectedArea; selectedArea.occupied = true; pollutedSpirits.push(polluted); game.addChild(polluted); // Spawn DarkSpirit behind polluted spirit var dark = new DarkSpirit(); dark.x = polluted.x - 50; dark.y = polluted.y + 30; dark.targetPlayer = player; darkSpirits.push(dark); game.addChild(dark); LK.getSound('darkspiritsound').play(); // Remove the used area from available areas emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1); } } } else if (currentLevel === 'level2') { // Level 2: Spawn both types if we have at least 2 empty areas if (emptyAreas.length >= 2) { // Spawn PollutedSpirit (pollutedspirit1) var selectedArea1 = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var polluted1 = new PollutedSpirit(); polluted1.x = selectedArea1.x; polluted1.y = selectedArea1.y; polluted1.assignedShowerArea = selectedArea1; selectedArea1.occupied = true; pollutedSpirits.push(polluted1); game.addChild(polluted1); // Remove the used area from available areas emptyAreas.splice(emptyAreas.indexOf(selectedArea1), 1); // Spawn PollutedSpirit2 (pollutedspirit2) var selectedArea2 = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var polluted2 = new PollutedSpirit2(); polluted2.x = selectedArea2.x; polluted2.y = selectedArea2.y; polluted2.assignedShowerArea = selectedArea2; selectedArea2.occupied = true; pollutedSpirits.push(polluted2); game.addChild(polluted2); // Spawn DarkSpirit behind polluted2 var dark2 = new DarkSpirit(); dark2.x = polluted2.x - 50; dark2.y = polluted2.y + 30; dark2.targetPlayer = player; darkSpirits.push(dark2); game.addChild(dark2); LK.getSound('darkspiritsound').play(); } else { // Only one empty area, spawn randomly either type var selectedArea = emptyAreas[0]; var polluted = Math.random() < 0.5 ? new PollutedSpirit() : new PollutedSpirit2(); polluted.x = selectedArea.x; polluted.y = selectedArea.y; polluted.assignedShowerArea = selectedArea; selectedArea.occupied = true; pollutedSpirits.push(polluted); game.addChild(polluted); } } else { // Level 1: Spawn only PollutedSpirit (pollutedspirit1) var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)]; var polluted = new PollutedSpirit(); polluted.x = selectedArea.x; polluted.y = selectedArea.y; polluted.assignedShowerArea = selectedArea; selectedArea.occupied = true; pollutedSpirits.push(polluted); game.addChild(polluted); // Spawn DarkSpirit behind polluted spirit in level1 var dark = new DarkSpirit(); dark.x = polluted.x - 50; dark.y = polluted.y + 30; dark.targetPlayer = player; darkSpirits.push(dark); game.addChild(dark); LK.getSound('darkspiritsound').play(); } // Move brush sprite to render on top of polluted spirits if (brushSprite) { game.removeChild(brushSprite); game.addChild(brushSprite); } // Ensure player renders on top of polluted spirits if (player) { game.removeChild(player); game.addChild(player); } } } function spawnRiverSpirit() { if (!riverSpirit) { riverSpirit = new RiverSpirit(); riverSpirit.x = -100; riverSpirit.y = Math.random() * 1000 + 1066; game.addChild(riverSpirit); } } function startLevel(levelName) { currentLevel = levelName; // Remove current background if (gameBackground) { gameBackground.destroy(); gameBackground = null; } // Stop current music and play appropriate level music LK.stopMusic(); if (levelName === 'level2') { LK.playMusic('level2music'); } else if (levelName === 'level3') { LK.playMusic('level3music'); } else if (levelName === 'level4') { LK.playMusic('level4music'); } else if (levelName === 'level1') { LK.playMusic('level1music'); } // Clear all existing enemies from previous level for (var i = blueSpirits.length - 1; i >= 0; i--) { blueSpirits[i].destroy(); } blueSpirits = []; for (var j = shadowSpirits.length - 1; j >= 0; j--) { shadowSpirits[j].destroy(); } shadowSpirits = []; for (var d = darkSpirits.length - 1; d >= 0; d--) { darkSpirits[d].destroy(); } darkSpirits = []; for (var k = pollutedSpirits.length - 1; k >= 0; k--) { if (pollutedSpirits[k].assignedShowerArea) { pollutedSpirits[k].assignedShowerArea.occupied = false; } pollutedSpirits[k].destroy(); } pollutedSpirits = []; if (riverSpirit) { riverSpirit.destroy(); riverSpirit = null; } // Remove existing shower areas for (var i = 0; i < showerAreas.length; i++) { showerAreas[i].destroy(); } showerAreas = []; // Add level background gameBackground = game.addChild(LK.getAsset('background02', { anchorX: 0, anchorY: 0, x: 0, y: 0, visible: true })); // Add shower areas based on level var screenMiddle = 1024; var spacing = 500; // 500px spacing between shower areas var positions = []; if (levelName === 'level2') { // Level 2: 4 shower areas in 2x2 grid positions = [{ x: screenMiddle - spacing / 2, y: 1366 - spacing / 2 }, { x: screenMiddle + spacing / 2, y: 1366 - spacing / 2 }, { x: screenMiddle - spacing / 2, y: 1366 + spacing / 2 }, { x: screenMiddle + spacing / 2, y: 1366 + spacing / 2 }]; } else if (levelName === 'level3') { // Level 3: 6 shower areas in 3x2 grid positions = [ // Top row (3 areas) { x: screenMiddle - spacing, y: 1366 - spacing / 2 }, { x: screenMiddle, y: 1366 - spacing / 2 }, { x: screenMiddle + spacing, y: 1366 - spacing / 2 }, // Bottom row (3 areas) { x: screenMiddle - spacing, y: 1366 + spacing / 2 }, { x: screenMiddle, y: 1366 + spacing / 2 }, { x: screenMiddle + spacing, y: 1366 + spacing / 2 }]; } else if (levelName === 'level4') { // Level 4: 6 shower areas in 2x3 grid positions = [ // Left column (3 areas) { x: screenMiddle - spacing / 2, y: 1366 - spacing }, { x: screenMiddle - spacing / 2, y: 1366 }, { x: screenMiddle - spacing / 2, y: 1366 + spacing }, // Right column (3 areas) { x: screenMiddle + spacing / 2, y: 1366 - spacing }, { x: screenMiddle + spacing / 2, y: 1366 }, { x: screenMiddle + spacing / 2, y: 1366 + spacing }]; } for (var p = 0; p < positions.length; p++) { var showerArea = game.addChild(LK.getAsset('Showerarea', { anchorX: 0.5, anchorY: 0.5, x: positions[p].x, y: positions[p].y })); showerArea.occupied = false; showerAreas.push(showerArea); } // Move player to front to ensure it renders above the new background game.removeChild(player); game.addChild(player); // Reset game state for new level player.health = player.maxHealth; healthBar.updateHealth(player.health); spawnInterval = 1200; // 20 seconds at 60fps shadowSpawnInterval = 240; if (levelName === 'level2') { maxShadowSpirits = 4; // Increase limit for level2 } else if (levelName === 'level3') { maxShadowSpirits = 6; // Increase limit for level3 } else if (levelName === 'level4') { maxShadowSpirits = 8; // Increase limit for level4 } } function initializeGame() { game.setBackgroundColor(0x16213e); currentLevel = 'level1'; // Add level1 game background gameBackground = game.addChild(LK.getAsset('gamebackground', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); player = game.addChild(new Player()); player.x = 1024; player.y = 2200; tokens = []; blueSpirits = []; shadowSpirits = []; pollutedSpirits = []; darkSpirits = []; riverSpirit = null; spawnTimer = 0; spawnInterval = 1200; // 20 seconds at 60fps for level1 shadowSpawnTimer = 0; shadowSpawnInterval = 360; // Level1 difficulty maxShadowSpirits = 2; // Level 1 limit shadowRespawnTimer = 0; scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); tokenTxt = new Text2('Tokens: 0', { size: 60, fill: 0xFFD700 }); tokenTxt.anchor.set(1, 0); tokenTxt.x = -20; LK.gui.topRight.addChild(tokenTxt); healthBar = new HealthBar(); healthBar.x = 20; healthBar.y = 30; LK.gui.topLeft.addChild(healthBar); // Initial spawns for (var i = 0; i < 5; i++) { spawnToken(); } spawnSpirit(); spawnPollutedSpirit(); // Initialize brush sprite (initially hidden) brushSprite = LK.getAsset('brush', { anchorX: 0.5, anchorY: 0.5 }); brushSprite.visible = false; game.addChild(brushSprite); // Initialize shower area system showerAreas = []; pollutedSpawnTimer = 0; pollutedSpawnInterval = 600; // 10 seconds at 60fps // Add shower areas based on level var screenMiddle = 1024; var spacing = 200; var showerAreaWidth = 300; // Based on asset width if (currentLevel === 'level1') { // Level 1: 2 shower areas var leftShowerArea = game.addChild(LK.getAsset('Showerarea', { anchorX: 0.5, anchorY: 0.5, x: screenMiddle - spacing / 2 - showerAreaWidth / 2, y: 1366 // Middle of screen height })); leftShowerArea.occupied = false; showerAreas.push(leftShowerArea); var rightShowerArea = game.addChild(LK.getAsset('Showerarea', { anchorX: 0.5, anchorY: 0.5, x: screenMiddle + spacing / 2 + showerAreaWidth / 2, y: 1366 // Middle of screen height })); rightShowerArea.occupied = false; showerAreas.push(rightShowerArea); } else if (currentLevel === 'level2') { // Level 2: 4 shower areas in 2x2 grid var spacing = 500; // 500px spacing between shower areas var positions = [{ x: screenMiddle - spacing / 2, y: 1366 - spacing / 2 }, { x: screenMiddle + spacing / 2, y: 1366 - spacing / 2 }, { x: screenMiddle - spacing / 2, y: 1366 + spacing / 2 }, { x: screenMiddle + spacing / 2, y: 1366 + spacing / 2 }]; for (var p = 0; p < positions.length; p++) { var showerArea = game.addChild(LK.getAsset('Showerarea', { anchorX: 0.5, anchorY: 0.5, x: positions[p].x, y: positions[p].y })); showerArea.occupied = false; showerAreas.push(showerArea); } } else if (currentLevel === 'level3') { // Level 3: 6 shower areas in 3x2 grid var spacing = 500; // 500px spacing between shower areas var positions = [ // Top row (3 areas) { x: screenMiddle - spacing, y: 1366 - spacing / 2 }, { x: screenMiddle, y: 1366 - spacing / 2 }, { x: screenMiddle + spacing, y: 1366 - spacing / 2 }, // Bottom row (3 areas) { x: screenMiddle - spacing, y: 1366 + spacing / 2 }, { x: screenMiddle, y: 1366 + spacing / 2 }, { x: screenMiddle + spacing, y: 1366 + spacing / 2 }]; for (var p = 0; p < positions.length; p++) { var showerArea = game.addChild(LK.getAsset('Showerarea', { anchorX: 0.5, anchorY: 0.5, x: positions[p].x, y: positions[p].y })); showerArea.occupied = false; showerAreas.push(showerArea); } } else if (currentLevel === 'level4') { // Level 4: 6 shower areas in 2x3 grid var spacing = 500; // 500px spacing between shower areas var positions = [ // Left column (3 areas) { x: screenMiddle - spacing / 2, y: 1366 - spacing }, { x: screenMiddle - spacing / 2, y: 1366 }, { x: screenMiddle - spacing / 2, y: 1366 + spacing }, // Right column (3 areas) { x: screenMiddle + spacing / 2, y: 1366 - spacing }, { x: screenMiddle + spacing / 2, y: 1366 }, { x: screenMiddle + spacing / 2, y: 1366 + spacing }]; for (var p = 0; p < positions.length; p++) { var showerArea = game.addChild(LK.getAsset('Showerarea', { anchorX: 0.5, anchorY: 0.5, x: positions[p].x, y: positions[p].y })); showerArea.occupied = false; showerAreas.push(showerArea); } } // Move player to front to ensure it renders above shower areas game.removeChild(player); game.addChild(player); } var dragActive = false; var currentMovementSprite = 'player'; var movementAnimationTimer = 0; var movementAnimationInterval = 15; // Switch every 15 frames (quarter second at 60fps) game.down = function (x, y, obj) { if (!gameStarted && startButton) { // Check if click is within start button bounds var buttonLeft = startButton.x - startButton.width * startButton.anchor.x; var buttonRight = startButton.x + startButton.width * (1 - startButton.anchor.x); var buttonTop = startButton.y - startButton.height * startButton.anchor.y; var buttonBottom = startButton.y + startButton.height * (1 - startButton.anchor.y); if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) { startGame(); return; } } if (gameStarted) { dragActive = true; } }; game.up = function (x, y, obj) { if (gameStarted) { dragActive = false; } }; game.move = function (x, y, obj) { if (gameStarted && dragActive && player) { player.x = x; player.y = y; // Throttle movement sound to prevent too frequent playback var currentTime = Date.now(); if (currentTime - lastMovementSoundTime >= movementSoundCooldown) { LK.getSound('playermovment').play(); lastMovementSoundTime = currentTime; } } }; game.update = function () { if (!gameStarted) { return; } // Update spawn timers spawnTimer++; shadowSpawnTimer++; if (spawnTimer >= spawnInterval) { spawnSpirit(); spawnTimer = 0; } if (shadowSpawnTimer >= shadowSpawnInterval) { spawnShadowSpirit(); shadowSpawnTimer = 0; } // Shadow spirit respawn timer shadowRespawnTimer++; if (shadowRespawnTimer >= shadowRespawnInterval && shadowSpirits.length < maxShadowSpirits) { spawnShadowSpirit(); shadowRespawnTimer = 0; } // Token spawning if (LK.ticks % 120 === 0) { spawnToken(); } // Polluted spirit spawning every 10 seconds pollutedSpawnTimer++; if (pollutedSpawnTimer >= pollutedSpawnInterval) { spawnPollutedSpirit(); pollutedSpawnTimer = 0; } // River spirit spawning if (LK.getScore() > 0 && LK.getScore() % 50 === 0 && !riverSpirit) { spawnRiverSpirit(); } // Check token collection for (var i = tokens.length - 1; i >= 0; i--) { var token = tokens[i]; if (!token.collected && player.intersects(token)) { token.collected = true; player.collectToken(); tokenTxt.setText('Tokens: ' + player.tokens); LK.getSound('collect').play(); token.destroy(); tokens.splice(i, 1); } } // Check spirit interactions for (var j = blueSpirits.length - 1; j >= 0; j--) { var spirit = blueSpirits[j]; if (!spirit.isDark && player.intersects(spirit) && player.tokens > 0) { player.tokens -= 1; // Only consume 1 token per spirit LK.setScore(LK.getScore() + 5); // Award 5 points per spirit freed scoreTxt.setText('Score: ' + LK.getScore()); tokenTxt.setText('Tokens: ' + player.tokens); LK.getSound('deliver').play(); // Remove one shadow spirit when blue spirit is freed if (shadowSpirits.length > 0) { var shadowToRemove = shadowSpirits[0]; shadowToRemove.destroy(); shadowSpirits.splice(0, 1); } spirit.destroy(); blueSpirits.splice(j, 1); } if (spirit.isDark && spirit.y > 2932) { spirit.destroy(); blueSpirits.splice(j, 1); } } // Check shadow spirit collisions for (var k = shadowSpirits.length - 1; k >= 0; k--) { var shadow = shadowSpirits[k]; if (shadow.lastIntersecting === undefined) { shadow.lastIntersecting = false; } var currentIntersecting = player.intersects(shadow); if (!shadow.lastIntersecting && currentIntersecting) { LK.getSound('damage').play(); var gameOver = player.takeDamage(); healthBar.updateHealth(player.health); if (gameOver) { LK.showGameOver(); } shadow.destroy(); shadowSpirits.splice(k, 1); continue; } shadow.lastIntersecting = currentIntersecting; } // Check dark spirit collisions for (var d = darkSpirits.length - 1; d >= 0; d--) { var darkSpirit = darkSpirits[d]; if (darkSpirit.lastIntersecting === undefined) { darkSpirit.lastIntersecting = false; } var currentDarkIntersecting = player.intersects(darkSpirit); if (!darkSpirit.lastIntersecting && currentDarkIntersecting) { LK.getSound('damage').play(); var gameOver = player.takeDamage(); healthBar.updateHealth(player.health); if (gameOver) { LK.showGameOver(); } darkSpirit.destroy(); darkSpirits.splice(d, 1); continue; } darkSpirit.lastIntersecting = currentDarkIntersecting; } // Check polluted spirit cleansing for (var l = pollutedSpirits.length - 1; l >= 0; l--) { var polluted = pollutedSpirits[l]; if (!polluted.isCleansed && player.intersects(polluted) && player.tokens >= polluted.tokensNeeded) { player.tokens -= polluted.tokensNeeded; tokenTxt.setText('Tokens: ' + player.tokens); LK.setScore(LK.getScore() + 15); scoreTxt.setText('Score: ' + LK.getScore()); LK.getSound('cleanse').play(); // Remove one darkspirit when polluted spirit is cleansed if (darkSpirits.length > 0) { var darkToRemove = darkSpirits[0]; darkToRemove.destroy(); darkSpirits.splice(0, 1); } // Show and animate brush if (brushSprite) { brushSprite.visible = true; brushSprite.x = polluted.x; brushSprite.y = polluted.y; // Stop any existing brush animations tween.stop(brushSprite); // Animate brush left and right to simulate brushing tween(brushSprite, { x: polluted.x - 30 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(brushSprite, { x: polluted.x + 30 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(brushSprite, { x: polluted.x - 20 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(brushSprite, { x: polluted.x + 20 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(brushSprite, { x: polluted.x }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { brushSprite.visible = false; } }); } }); } }); } }); } }); } // Free up the shower area if (polluted.assignedShowerArea) { polluted.assignedShowerArea.occupied = false; } polluted.cleanse(); pollutedSpirits.splice(l, 1); } } // Check river spirit collection if (riverSpirit) { if (riverSpirit.lastIntersecting === undefined) { riverSpirit.lastIntersecting = false; } var riverIntersecting = player.intersects(riverSpirit); if (!riverSpirit.lastIntersecting && riverIntersecting) { LK.setScore(LK.getScore() + riverSpirit.bonusValue); scoreTxt.setText('Score: ' + LK.getScore()); // Regenerate health by 30% player.health += player.maxHealth * 0.3; player.health = Math.min(player.health, player.maxHealth); // Cap at max health healthBar.updateHealth(player.health); LK.effects.flashObject(player, 0x87ceeb, 1000); // Flash blue to indicate healing riverSpirit.destroy(); riverSpirit = null; } else if (riverSpirit && riverSpirit.x > 2200) { riverSpirit.destroy(); riverSpirit = null; } if (riverSpirit) { riverSpirit.lastIntersecting = riverIntersecting; } } // Handle movement animation when dragging if (dragActive && player) { movementAnimationTimer++; if (movementAnimationTimer >= movementAnimationInterval) { // Switch between movement sprites if (currentMovementSprite === 'player') { currentMovementSprite = 'playermovement1'; } else if (currentMovementSprite === 'playermovement1') { currentMovementSprite = 'playermovement2'; } else { currentMovementSprite = 'playermovement1'; } // Update player sprite player.removeChildren(); var newPlayerGraphics = player.attachAsset(currentMovementSprite, { anchorX: 0.5, anchorY: 0.5 }); movementAnimationTimer = 0; } } else if (!dragActive && currentMovementSprite !== 'player') { // Reset to normal player sprite when not dragging currentMovementSprite = 'player'; player.removeChildren(); var normalPlayerGraphics = player.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); } // Player direction based on position var screenMiddle = 1024; if (player.x < screenMiddle) { // Player is on left side, face left (flip horizontally) player.scaleX = -1; } else { // Player is on right side, face right (normal orientation) player.scaleX = 1; } // Level progression if (currentLevel === 'level1' && LK.getScore() >= 250) { startLevel('level2'); } if (currentLevel === 'level2' && LK.getScore() >= 500) { startLevel('level3'); } if (currentLevel === 'level3' && LK.getScore() >= 750) { startLevel('level4'); } // Win condition if (currentLevel === 'level4' && LK.getScore() >= 1000) { LK.showYouWin(); } }; // Show intro screen initially showIntroScreen();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BlueSpirit = Container.expand(function () {
var self = Container.call(this);
var spiritGraphics = self.attachAsset('blueSpirit', {
anchorX: 0.5,
anchorY: 0.5
});
self.waitTime = 0;
self.maxWaitTime = 600; // 10 seconds at 60fps (default for both levels)
self.isDark = false;
self.speed = 1;
self.update = function () {
self.waitTime++;
// Set maxWaitTime based on current level
if (currentLevel === 'level1') {
self.maxWaitTime = 600; // 10 seconds at 60fps
} else if (currentLevel === 'level2') {
self.maxWaitTime = 600; // 10 seconds at 60fps
}
if (!self.isDark && self.waitTime >= self.maxWaitTime) {
self.turnDark();
}
if (self.isDark) {
self.y += self.speed;
}
};
self.turnDark = function () {
self.isDark = true;
spiritGraphics.tint = 0x2a0845;
tween(spiritGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500
});
};
return self;
});
var DarkSpirit = Container.expand(function () {
var self = Container.call(this);
var darkGraphics = self.attachAsset('darkSpirit', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.targetPlayer = null;
self.update = function () {
if (self.targetPlayer) {
var dx = self.targetPlayer.x - self.x;
var dy = self.targetPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Change direction based on position relative to screen middle
var screenMiddle = 1024;
if (self.x < screenMiddle) {
// Dark spirit is on left side, face right towards middle
self.scaleX = 1;
} else {
// Dark spirit is on right side, face left towards middle
self.scaleX = -1;
}
}
};
return self;
});
var HealthBar = Container.expand(function () {
var self = Container.call(this);
// Background bar
self.bgBar = self.attachAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
// Health bar
self.healthBar = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0
});
self.maxWidth = 300;
self.updateHealth = function (healthPercent) {
self.healthBar.scaleX = healthPercent / 100;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokens = 0;
self.health = 100;
self.maxHealth = 100;
self.collectToken = function () {
self.tokens++;
};
self.deliverTokens = function () {
var delivered = self.tokens;
self.tokens = 0;
return delivered;
};
self.takeDamage = function () {
var damage = Math.random() * 7 + 3; // Random between 3-10
self.health -= damage;
self.health = Math.max(0, self.health); // Ensure health doesn't go below 0
LK.effects.flashObject(self, 0xff0000, 500);
return self.health <= 0;
};
return self;
});
var PollutedSpirit = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit1', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit2 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit2', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit3 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit3', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit4 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit4', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit5 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit5', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit6 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit6', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit7 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit7', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PollutedSpirit8 = Container.expand(function () {
var self = Container.call(this);
var pollutedGraphics = self.attachAsset('pollutedspirit8', {
anchorX: 0.5,
anchorY: 0.5
});
self.tokensNeeded = 3;
self.isCleansed = false;
self.cleanse = function () {
self.isCleansed = true;
tween(pollutedGraphics, {
tint: 0x98fb98
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var RiverSpirit = Container.expand(function () {
var self = Container.call(this);
var riverGraphics = self.attachAsset('riverSpirit', {
anchorX: 0.5,
anchorY: 0.5
});
self.bonusValue = 20;
self.speed = 0.5;
self.update = function () {
self.x += self.speed;
self.rotation += 0.01;
};
return self;
});
var ShadowSpirit = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('shadowSpirit', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.targetPlayer = null;
self.update = function () {
if (self.targetPlayer) {
var dx = self.targetPlayer.x - self.x;
var dy = self.targetPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Change direction based on position relative to screen middle
var screenMiddle = 1024;
if (self.x < screenMiddle) {
// Shadow is on left side, face right towards middle
self.scaleX = 1;
} else {
// Shadow is on right side, face left towards middle
self.scaleX = -1;
}
}
};
return self;
});
var Token = Container.expand(function () {
var self = Container.call(this);
var tokenGraphics = self.attachAsset('token', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
if (!self.collected) {
self.rotation += 0.02;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var gameStarted = false;
var introBackground = null;
var startButton = null;
var currentLevel = 'level1';
var gameBackground = null;
// Declare game variables in global scope
var player, tokens, blueSpirits, shadowSpirits, pollutedSpirits, riverSpirit, darkSpirits;
var spawnTimer, spawnInterval, shadowSpawnTimer, shadowSpawnInterval;
var scoreTxt, tokenTxt, healthBar;
var brushSprite = null;
var showerAreas = [],
pollutedSpawnTimer,
pollutedSpawnInterval;
var maxShadowSpirits = 2; // Level 1: 2, Level 2: 4
var shadowRespawnTimer = 0;
var shadowRespawnInterval = 180; // 3 seconds to respawn
var lastMovementSoundTime = 0;
var movementSoundCooldown = 1000; // Milliseconds between sound plays
// Show intro screen
function showIntroScreen() {
introBackground = game.addChild(LK.getAsset('introbackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
startButton = game.addChild(LK.getAsset('startbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2400
}));
// Only play intro music if game hasn't started and we're actually showing intro screen
if (!gameStarted && introBackground && startButton) {
LK.playMusic('intromusic');
}
}
// Start the actual game
function startGame() {
gameStarted = true;
if (introBackground) {
introBackground.destroy();
introBackground = null;
}
if (startButton) {
startButton.destroy();
startButton = null;
}
LK.stopMusic();
LK.playMusic('level1music');
initializeGame();
}
// Spawn functions in global scope
function spawnToken() {
var token = new Token();
token.x = Math.random() * 1848 + 100;
token.y = Math.random() * 2332 + 300;
tokens.push(token);
game.addChild(token);
}
function spawnSpirit() {
var spirit = new BlueSpirit();
spirit.x = Math.random() * 1848 + 100;
spirit.y = 300;
blueSpirits.push(spirit);
game.addChild(spirit);
}
function spawnShadowSpirit() {
// Only spawn if under the limit
if (shadowSpirits.length < maxShadowSpirits) {
var shadow = new ShadowSpirit();
shadow.x = Math.random() * 1848 + 100;
shadow.y = Math.random() * 400 + 300;
shadow.targetPlayer = player;
shadowSpirits.push(shadow);
game.addChild(shadow);
LK.getSound('Shadowspiritsound').play();
}
}
function spawnPollutedSpirit() {
// Find an empty shower area
var emptyAreas = [];
for (var i = 0; i < showerAreas.length; i++) {
if (!showerAreas[i].occupied) {
emptyAreas.push(showerAreas[i]);
}
}
// Only spawn if there's an empty area
if (emptyAreas.length > 0) {
if (currentLevel === 'level4') {
// Level 4: Spawn only PollutedSpirit6, PollutedSpirit7, PollutedSpirit8
if (emptyAreas.length >= 3) {
var pollutedTypes = [PollutedSpirit6, PollutedSpirit7, PollutedSpirit8];
for (var t = 0; t < pollutedTypes.length; t++) {
var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var polluted = new pollutedTypes[t]();
polluted.x = selectedArea.x;
polluted.y = selectedArea.y;
polluted.assignedShowerArea = selectedArea;
selectedArea.occupied = true;
pollutedSpirits.push(polluted);
game.addChild(polluted);
// Spawn DarkSpirit behind polluted spirit
var dark = new DarkSpirit();
dark.x = polluted.x - 50;
dark.y = polluted.y + 30;
dark.targetPlayer = player;
darkSpirits.push(dark);
game.addChild(dark);
LK.getSound('darkspiritsound').play();
// Remove the used area from available areas
emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1);
}
} else {
// Spawn random types based on available areas
var pollutedTypes = [PollutedSpirit6, PollutedSpirit7, PollutedSpirit8];
for (var a = 0; a < Math.min(emptyAreas.length, 3); a++) {
var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var randomType = pollutedTypes[Math.floor(Math.random() * pollutedTypes.length)];
var polluted = new randomType();
polluted.x = selectedArea.x;
polluted.y = selectedArea.y;
polluted.assignedShowerArea = selectedArea;
selectedArea.occupied = true;
pollutedSpirits.push(polluted);
game.addChild(polluted);
// Spawn DarkSpirit behind polluted spirit
var dark = new DarkSpirit();
dark.x = polluted.x - 50;
dark.y = polluted.y + 30;
dark.targetPlayer = player;
darkSpirits.push(dark);
game.addChild(dark);
LK.getSound('darkspiritsound').play();
// Remove the used area from available areas
emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1);
}
}
} else if (currentLevel === 'level3') {
// Level 3: Spawn all 5 types if we have at least 5 empty areas
if (emptyAreas.length >= 5) {
var pollutedTypes = [PollutedSpirit, PollutedSpirit2, PollutedSpirit3, PollutedSpirit4, PollutedSpirit5];
for (var t = 0; t < pollutedTypes.length; t++) {
var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var polluted = new pollutedTypes[t]();
polluted.x = selectedArea.x;
polluted.y = selectedArea.y;
polluted.assignedShowerArea = selectedArea;
selectedArea.occupied = true;
pollutedSpirits.push(polluted);
game.addChild(polluted);
// Spawn DarkSpirit behind polluted spirit
var dark = new DarkSpirit();
dark.x = polluted.x - 50;
dark.y = polluted.y + 30;
dark.targetPlayer = player;
darkSpirits.push(dark);
game.addChild(dark);
LK.getSound('darkspiritsound').play();
// Remove the used area from available areas
emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1);
}
} else {
// Spawn random types based on available areas
var pollutedTypes = [PollutedSpirit, PollutedSpirit2, PollutedSpirit3, PollutedSpirit4, PollutedSpirit5];
for (var a = 0; a < Math.min(emptyAreas.length, 3); a++) {
var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var randomType = pollutedTypes[Math.floor(Math.random() * pollutedTypes.length)];
var polluted = new randomType();
polluted.x = selectedArea.x;
polluted.y = selectedArea.y;
polluted.assignedShowerArea = selectedArea;
selectedArea.occupied = true;
pollutedSpirits.push(polluted);
game.addChild(polluted);
// Spawn DarkSpirit behind polluted spirit
var dark = new DarkSpirit();
dark.x = polluted.x - 50;
dark.y = polluted.y + 30;
dark.targetPlayer = player;
darkSpirits.push(dark);
game.addChild(dark);
LK.getSound('darkspiritsound').play();
// Remove the used area from available areas
emptyAreas.splice(emptyAreas.indexOf(selectedArea), 1);
}
}
} else if (currentLevel === 'level2') {
// Level 2: Spawn both types if we have at least 2 empty areas
if (emptyAreas.length >= 2) {
// Spawn PollutedSpirit (pollutedspirit1)
var selectedArea1 = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var polluted1 = new PollutedSpirit();
polluted1.x = selectedArea1.x;
polluted1.y = selectedArea1.y;
polluted1.assignedShowerArea = selectedArea1;
selectedArea1.occupied = true;
pollutedSpirits.push(polluted1);
game.addChild(polluted1);
// Remove the used area from available areas
emptyAreas.splice(emptyAreas.indexOf(selectedArea1), 1);
// Spawn PollutedSpirit2 (pollutedspirit2)
var selectedArea2 = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var polluted2 = new PollutedSpirit2();
polluted2.x = selectedArea2.x;
polluted2.y = selectedArea2.y;
polluted2.assignedShowerArea = selectedArea2;
selectedArea2.occupied = true;
pollutedSpirits.push(polluted2);
game.addChild(polluted2);
// Spawn DarkSpirit behind polluted2
var dark2 = new DarkSpirit();
dark2.x = polluted2.x - 50;
dark2.y = polluted2.y + 30;
dark2.targetPlayer = player;
darkSpirits.push(dark2);
game.addChild(dark2);
LK.getSound('darkspiritsound').play();
} else {
// Only one empty area, spawn randomly either type
var selectedArea = emptyAreas[0];
var polluted = Math.random() < 0.5 ? new PollutedSpirit() : new PollutedSpirit2();
polluted.x = selectedArea.x;
polluted.y = selectedArea.y;
polluted.assignedShowerArea = selectedArea;
selectedArea.occupied = true;
pollutedSpirits.push(polluted);
game.addChild(polluted);
}
} else {
// Level 1: Spawn only PollutedSpirit (pollutedspirit1)
var selectedArea = emptyAreas[Math.floor(Math.random() * emptyAreas.length)];
var polluted = new PollutedSpirit();
polluted.x = selectedArea.x;
polluted.y = selectedArea.y;
polluted.assignedShowerArea = selectedArea;
selectedArea.occupied = true;
pollutedSpirits.push(polluted);
game.addChild(polluted);
// Spawn DarkSpirit behind polluted spirit in level1
var dark = new DarkSpirit();
dark.x = polluted.x - 50;
dark.y = polluted.y + 30;
dark.targetPlayer = player;
darkSpirits.push(dark);
game.addChild(dark);
LK.getSound('darkspiritsound').play();
}
// Move brush sprite to render on top of polluted spirits
if (brushSprite) {
game.removeChild(brushSprite);
game.addChild(brushSprite);
}
// Ensure player renders on top of polluted spirits
if (player) {
game.removeChild(player);
game.addChild(player);
}
}
}
function spawnRiverSpirit() {
if (!riverSpirit) {
riverSpirit = new RiverSpirit();
riverSpirit.x = -100;
riverSpirit.y = Math.random() * 1000 + 1066;
game.addChild(riverSpirit);
}
}
function startLevel(levelName) {
currentLevel = levelName;
// Remove current background
if (gameBackground) {
gameBackground.destroy();
gameBackground = null;
}
// Stop current music and play appropriate level music
LK.stopMusic();
if (levelName === 'level2') {
LK.playMusic('level2music');
} else if (levelName === 'level3') {
LK.playMusic('level3music');
} else if (levelName === 'level4') {
LK.playMusic('level4music');
} else if (levelName === 'level1') {
LK.playMusic('level1music');
}
// Clear all existing enemies from previous level
for (var i = blueSpirits.length - 1; i >= 0; i--) {
blueSpirits[i].destroy();
}
blueSpirits = [];
for (var j = shadowSpirits.length - 1; j >= 0; j--) {
shadowSpirits[j].destroy();
}
shadowSpirits = [];
for (var d = darkSpirits.length - 1; d >= 0; d--) {
darkSpirits[d].destroy();
}
darkSpirits = [];
for (var k = pollutedSpirits.length - 1; k >= 0; k--) {
if (pollutedSpirits[k].assignedShowerArea) {
pollutedSpirits[k].assignedShowerArea.occupied = false;
}
pollutedSpirits[k].destroy();
}
pollutedSpirits = [];
if (riverSpirit) {
riverSpirit.destroy();
riverSpirit = null;
}
// Remove existing shower areas
for (var i = 0; i < showerAreas.length; i++) {
showerAreas[i].destroy();
}
showerAreas = [];
// Add level background
gameBackground = game.addChild(LK.getAsset('background02', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
visible: true
}));
// Add shower areas based on level
var screenMiddle = 1024;
var spacing = 500; // 500px spacing between shower areas
var positions = [];
if (levelName === 'level2') {
// Level 2: 4 shower areas in 2x2 grid
positions = [{
x: screenMiddle - spacing / 2,
y: 1366 - spacing / 2
}, {
x: screenMiddle + spacing / 2,
y: 1366 - spacing / 2
}, {
x: screenMiddle - spacing / 2,
y: 1366 + spacing / 2
}, {
x: screenMiddle + spacing / 2,
y: 1366 + spacing / 2
}];
} else if (levelName === 'level3') {
// Level 3: 6 shower areas in 3x2 grid
positions = [
// Top row (3 areas)
{
x: screenMiddle - spacing,
y: 1366 - spacing / 2
}, {
x: screenMiddle,
y: 1366 - spacing / 2
}, {
x: screenMiddle + spacing,
y: 1366 - spacing / 2
},
// Bottom row (3 areas)
{
x: screenMiddle - spacing,
y: 1366 + spacing / 2
}, {
x: screenMiddle,
y: 1366 + spacing / 2
}, {
x: screenMiddle + spacing,
y: 1366 + spacing / 2
}];
} else if (levelName === 'level4') {
// Level 4: 6 shower areas in 2x3 grid
positions = [
// Left column (3 areas)
{
x: screenMiddle - spacing / 2,
y: 1366 - spacing
}, {
x: screenMiddle - spacing / 2,
y: 1366
}, {
x: screenMiddle - spacing / 2,
y: 1366 + spacing
},
// Right column (3 areas)
{
x: screenMiddle + spacing / 2,
y: 1366 - spacing
}, {
x: screenMiddle + spacing / 2,
y: 1366
}, {
x: screenMiddle + spacing / 2,
y: 1366 + spacing
}];
}
for (var p = 0; p < positions.length; p++) {
var showerArea = game.addChild(LK.getAsset('Showerarea', {
anchorX: 0.5,
anchorY: 0.5,
x: positions[p].x,
y: positions[p].y
}));
showerArea.occupied = false;
showerAreas.push(showerArea);
}
// Move player to front to ensure it renders above the new background
game.removeChild(player);
game.addChild(player);
// Reset game state for new level
player.health = player.maxHealth;
healthBar.updateHealth(player.health);
spawnInterval = 1200; // 20 seconds at 60fps
shadowSpawnInterval = 240;
if (levelName === 'level2') {
maxShadowSpirits = 4; // Increase limit for level2
} else if (levelName === 'level3') {
maxShadowSpirits = 6; // Increase limit for level3
} else if (levelName === 'level4') {
maxShadowSpirits = 8; // Increase limit for level4
}
}
function initializeGame() {
game.setBackgroundColor(0x16213e);
currentLevel = 'level1';
// Add level1 game background
gameBackground = game.addChild(LK.getAsset('gamebackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
player = game.addChild(new Player());
player.x = 1024;
player.y = 2200;
tokens = [];
blueSpirits = [];
shadowSpirits = [];
pollutedSpirits = [];
darkSpirits = [];
riverSpirit = null;
spawnTimer = 0;
spawnInterval = 1200; // 20 seconds at 60fps for level1
shadowSpawnTimer = 0;
shadowSpawnInterval = 360; // Level1 difficulty
maxShadowSpirits = 2; // Level 1 limit
shadowRespawnTimer = 0;
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
tokenTxt = new Text2('Tokens: 0', {
size: 60,
fill: 0xFFD700
});
tokenTxt.anchor.set(1, 0);
tokenTxt.x = -20;
LK.gui.topRight.addChild(tokenTxt);
healthBar = new HealthBar();
healthBar.x = 20;
healthBar.y = 30;
LK.gui.topLeft.addChild(healthBar);
// Initial spawns
for (var i = 0; i < 5; i++) {
spawnToken();
}
spawnSpirit();
spawnPollutedSpirit();
// Initialize brush sprite (initially hidden)
brushSprite = LK.getAsset('brush', {
anchorX: 0.5,
anchorY: 0.5
});
brushSprite.visible = false;
game.addChild(brushSprite);
// Initialize shower area system
showerAreas = [];
pollutedSpawnTimer = 0;
pollutedSpawnInterval = 600; // 10 seconds at 60fps
// Add shower areas based on level
var screenMiddle = 1024;
var spacing = 200;
var showerAreaWidth = 300; // Based on asset width
if (currentLevel === 'level1') {
// Level 1: 2 shower areas
var leftShowerArea = game.addChild(LK.getAsset('Showerarea', {
anchorX: 0.5,
anchorY: 0.5,
x: screenMiddle - spacing / 2 - showerAreaWidth / 2,
y: 1366 // Middle of screen height
}));
leftShowerArea.occupied = false;
showerAreas.push(leftShowerArea);
var rightShowerArea = game.addChild(LK.getAsset('Showerarea', {
anchorX: 0.5,
anchorY: 0.5,
x: screenMiddle + spacing / 2 + showerAreaWidth / 2,
y: 1366 // Middle of screen height
}));
rightShowerArea.occupied = false;
showerAreas.push(rightShowerArea);
} else if (currentLevel === 'level2') {
// Level 2: 4 shower areas in 2x2 grid
var spacing = 500; // 500px spacing between shower areas
var positions = [{
x: screenMiddle - spacing / 2,
y: 1366 - spacing / 2
}, {
x: screenMiddle + spacing / 2,
y: 1366 - spacing / 2
}, {
x: screenMiddle - spacing / 2,
y: 1366 + spacing / 2
}, {
x: screenMiddle + spacing / 2,
y: 1366 + spacing / 2
}];
for (var p = 0; p < positions.length; p++) {
var showerArea = game.addChild(LK.getAsset('Showerarea', {
anchorX: 0.5,
anchorY: 0.5,
x: positions[p].x,
y: positions[p].y
}));
showerArea.occupied = false;
showerAreas.push(showerArea);
}
} else if (currentLevel === 'level3') {
// Level 3: 6 shower areas in 3x2 grid
var spacing = 500; // 500px spacing between shower areas
var positions = [
// Top row (3 areas)
{
x: screenMiddle - spacing,
y: 1366 - spacing / 2
}, {
x: screenMiddle,
y: 1366 - spacing / 2
}, {
x: screenMiddle + spacing,
y: 1366 - spacing / 2
},
// Bottom row (3 areas)
{
x: screenMiddle - spacing,
y: 1366 + spacing / 2
}, {
x: screenMiddle,
y: 1366 + spacing / 2
}, {
x: screenMiddle + spacing,
y: 1366 + spacing / 2
}];
for (var p = 0; p < positions.length; p++) {
var showerArea = game.addChild(LK.getAsset('Showerarea', {
anchorX: 0.5,
anchorY: 0.5,
x: positions[p].x,
y: positions[p].y
}));
showerArea.occupied = false;
showerAreas.push(showerArea);
}
} else if (currentLevel === 'level4') {
// Level 4: 6 shower areas in 2x3 grid
var spacing = 500; // 500px spacing between shower areas
var positions = [
// Left column (3 areas)
{
x: screenMiddle - spacing / 2,
y: 1366 - spacing
}, {
x: screenMiddle - spacing / 2,
y: 1366
}, {
x: screenMiddle - spacing / 2,
y: 1366 + spacing
},
// Right column (3 areas)
{
x: screenMiddle + spacing / 2,
y: 1366 - spacing
}, {
x: screenMiddle + spacing / 2,
y: 1366
}, {
x: screenMiddle + spacing / 2,
y: 1366 + spacing
}];
for (var p = 0; p < positions.length; p++) {
var showerArea = game.addChild(LK.getAsset('Showerarea', {
anchorX: 0.5,
anchorY: 0.5,
x: positions[p].x,
y: positions[p].y
}));
showerArea.occupied = false;
showerAreas.push(showerArea);
}
}
// Move player to front to ensure it renders above shower areas
game.removeChild(player);
game.addChild(player);
}
var dragActive = false;
var currentMovementSprite = 'player';
var movementAnimationTimer = 0;
var movementAnimationInterval = 15; // Switch every 15 frames (quarter second at 60fps)
game.down = function (x, y, obj) {
if (!gameStarted && startButton) {
// Check if click is within start button bounds
var buttonLeft = startButton.x - startButton.width * startButton.anchor.x;
var buttonRight = startButton.x + startButton.width * (1 - startButton.anchor.x);
var buttonTop = startButton.y - startButton.height * startButton.anchor.y;
var buttonBottom = startButton.y + startButton.height * (1 - startButton.anchor.y);
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
startGame();
return;
}
}
if (gameStarted) {
dragActive = true;
}
};
game.up = function (x, y, obj) {
if (gameStarted) {
dragActive = false;
}
};
game.move = function (x, y, obj) {
if (gameStarted && dragActive && player) {
player.x = x;
player.y = y;
// Throttle movement sound to prevent too frequent playback
var currentTime = Date.now();
if (currentTime - lastMovementSoundTime >= movementSoundCooldown) {
LK.getSound('playermovment').play();
lastMovementSoundTime = currentTime;
}
}
};
game.update = function () {
if (!gameStarted) {
return;
}
// Update spawn timers
spawnTimer++;
shadowSpawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnSpirit();
spawnTimer = 0;
}
if (shadowSpawnTimer >= shadowSpawnInterval) {
spawnShadowSpirit();
shadowSpawnTimer = 0;
}
// Shadow spirit respawn timer
shadowRespawnTimer++;
if (shadowRespawnTimer >= shadowRespawnInterval && shadowSpirits.length < maxShadowSpirits) {
spawnShadowSpirit();
shadowRespawnTimer = 0;
}
// Token spawning
if (LK.ticks % 120 === 0) {
spawnToken();
}
// Polluted spirit spawning every 10 seconds
pollutedSpawnTimer++;
if (pollutedSpawnTimer >= pollutedSpawnInterval) {
spawnPollutedSpirit();
pollutedSpawnTimer = 0;
}
// River spirit spawning
if (LK.getScore() > 0 && LK.getScore() % 50 === 0 && !riverSpirit) {
spawnRiverSpirit();
}
// Check token collection
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (!token.collected && player.intersects(token)) {
token.collected = true;
player.collectToken();
tokenTxt.setText('Tokens: ' + player.tokens);
LK.getSound('collect').play();
token.destroy();
tokens.splice(i, 1);
}
}
// Check spirit interactions
for (var j = blueSpirits.length - 1; j >= 0; j--) {
var spirit = blueSpirits[j];
if (!spirit.isDark && player.intersects(spirit) && player.tokens > 0) {
player.tokens -= 1; // Only consume 1 token per spirit
LK.setScore(LK.getScore() + 5); // Award 5 points per spirit freed
scoreTxt.setText('Score: ' + LK.getScore());
tokenTxt.setText('Tokens: ' + player.tokens);
LK.getSound('deliver').play();
// Remove one shadow spirit when blue spirit is freed
if (shadowSpirits.length > 0) {
var shadowToRemove = shadowSpirits[0];
shadowToRemove.destroy();
shadowSpirits.splice(0, 1);
}
spirit.destroy();
blueSpirits.splice(j, 1);
}
if (spirit.isDark && spirit.y > 2932) {
spirit.destroy();
blueSpirits.splice(j, 1);
}
}
// Check shadow spirit collisions
for (var k = shadowSpirits.length - 1; k >= 0; k--) {
var shadow = shadowSpirits[k];
if (shadow.lastIntersecting === undefined) {
shadow.lastIntersecting = false;
}
var currentIntersecting = player.intersects(shadow);
if (!shadow.lastIntersecting && currentIntersecting) {
LK.getSound('damage').play();
var gameOver = player.takeDamage();
healthBar.updateHealth(player.health);
if (gameOver) {
LK.showGameOver();
}
shadow.destroy();
shadowSpirits.splice(k, 1);
continue;
}
shadow.lastIntersecting = currentIntersecting;
}
// Check dark spirit collisions
for (var d = darkSpirits.length - 1; d >= 0; d--) {
var darkSpirit = darkSpirits[d];
if (darkSpirit.lastIntersecting === undefined) {
darkSpirit.lastIntersecting = false;
}
var currentDarkIntersecting = player.intersects(darkSpirit);
if (!darkSpirit.lastIntersecting && currentDarkIntersecting) {
LK.getSound('damage').play();
var gameOver = player.takeDamage();
healthBar.updateHealth(player.health);
if (gameOver) {
LK.showGameOver();
}
darkSpirit.destroy();
darkSpirits.splice(d, 1);
continue;
}
darkSpirit.lastIntersecting = currentDarkIntersecting;
}
// Check polluted spirit cleansing
for (var l = pollutedSpirits.length - 1; l >= 0; l--) {
var polluted = pollutedSpirits[l];
if (!polluted.isCleansed && player.intersects(polluted) && player.tokens >= polluted.tokensNeeded) {
player.tokens -= polluted.tokensNeeded;
tokenTxt.setText('Tokens: ' + player.tokens);
LK.setScore(LK.getScore() + 15);
scoreTxt.setText('Score: ' + LK.getScore());
LK.getSound('cleanse').play();
// Remove one darkspirit when polluted spirit is cleansed
if (darkSpirits.length > 0) {
var darkToRemove = darkSpirits[0];
darkToRemove.destroy();
darkSpirits.splice(0, 1);
}
// Show and animate brush
if (brushSprite) {
brushSprite.visible = true;
brushSprite.x = polluted.x;
brushSprite.y = polluted.y;
// Stop any existing brush animations
tween.stop(brushSprite);
// Animate brush left and right to simulate brushing
tween(brushSprite, {
x: polluted.x - 30
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(brushSprite, {
x: polluted.x + 30
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(brushSprite, {
x: polluted.x - 20
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(brushSprite, {
x: polluted.x + 20
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(brushSprite, {
x: polluted.x
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
brushSprite.visible = false;
}
});
}
});
}
});
}
});
}
});
}
// Free up the shower area
if (polluted.assignedShowerArea) {
polluted.assignedShowerArea.occupied = false;
}
polluted.cleanse();
pollutedSpirits.splice(l, 1);
}
}
// Check river spirit collection
if (riverSpirit) {
if (riverSpirit.lastIntersecting === undefined) {
riverSpirit.lastIntersecting = false;
}
var riverIntersecting = player.intersects(riverSpirit);
if (!riverSpirit.lastIntersecting && riverIntersecting) {
LK.setScore(LK.getScore() + riverSpirit.bonusValue);
scoreTxt.setText('Score: ' + LK.getScore());
// Regenerate health by 30%
player.health += player.maxHealth * 0.3;
player.health = Math.min(player.health, player.maxHealth); // Cap at max health
healthBar.updateHealth(player.health);
LK.effects.flashObject(player, 0x87ceeb, 1000); // Flash blue to indicate healing
riverSpirit.destroy();
riverSpirit = null;
} else if (riverSpirit && riverSpirit.x > 2200) {
riverSpirit.destroy();
riverSpirit = null;
}
if (riverSpirit) {
riverSpirit.lastIntersecting = riverIntersecting;
}
}
// Handle movement animation when dragging
if (dragActive && player) {
movementAnimationTimer++;
if (movementAnimationTimer >= movementAnimationInterval) {
// Switch between movement sprites
if (currentMovementSprite === 'player') {
currentMovementSprite = 'playermovement1';
} else if (currentMovementSprite === 'playermovement1') {
currentMovementSprite = 'playermovement2';
} else {
currentMovementSprite = 'playermovement1';
}
// Update player sprite
player.removeChildren();
var newPlayerGraphics = player.attachAsset(currentMovementSprite, {
anchorX: 0.5,
anchorY: 0.5
});
movementAnimationTimer = 0;
}
} else if (!dragActive && currentMovementSprite !== 'player') {
// Reset to normal player sprite when not dragging
currentMovementSprite = 'player';
player.removeChildren();
var normalPlayerGraphics = player.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Player direction based on position
var screenMiddle = 1024;
if (player.x < screenMiddle) {
// Player is on left side, face left (flip horizontally)
player.scaleX = -1;
} else {
// Player is on right side, face right (normal orientation)
player.scaleX = 1;
}
// Level progression
if (currentLevel === 'level1' && LK.getScore() >= 250) {
startLevel('level2');
}
if (currentLevel === 'level2' && LK.getScore() >= 500) {
startLevel('level3');
}
if (currentLevel === 'level3' && LK.getScore() >= 750) {
startLevel('level4');
}
// Win condition
if (currentLevel === 'level4' && LK.getScore() >= 1000) {
LK.showYouWin();
}
};
// Show intro screen initially
showIntroScreen();