/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { currentLevel: 1, highestLevel: 1 }); /**** * Classes ****/ var GameObject = Container.expand(function (id, width, height) { var self = Container.call(this); self.id = id; self.width = width; self.height = height; self.isDestroyed = false; self.destroy = function () { self.isDestroyed = true; Container.prototype.destroy.call(self); }; return self; }); var WaterParticle = GameObject.expand(function (x, y) { var self = GameObject.call(this, 'water', 20, 20); var graphics = self.attachAsset('water', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.lifespan = 30; // frames self.velocity = { x: Math.random() * 6 - 3, y: -8 - Math.random() * 3 }; self.update = function () { self.x += self.velocity.x; self.y += self.velocity.y; self.lifespan--; // Apply gravity self.velocity.y += 0.3; // Fade out graphics.alpha = self.lifespan / 30; // Check collision with fires for (var i = fires.length - 1; i >= 0; i--) { var fire = fires[i]; if (self.intersects(fire)) { fire.intensity -= 5; if (fire.intensity <= 0) { LK.getSound('extinguish').play(); LK.setScore(LK.getScore() + 50); updateScoreText(); fires.splice(i, 1); fire.destroy(); } self.lifespan = 0; break; } } if (self.lifespan <= 0) { self.destroy(); for (var i = 0; i < waterParticles.length; i++) { if (waterParticles[i] === self) { waterParticles.splice(i, 1); break; } } } }; return self; }); var Wall = GameObject.expand(function (x, y, width, height) { var self = GameObject.call(this, 'wall', width, height); var graphics = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, width: width, height: height }); self.x = x; self.y = y; return self; }); var Smoke = GameObject.expand(function (x, y) { var self = GameObject.call(this, 'smoke', 200, 200); var graphics = self.attachAsset('smoke', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); self.x = x; self.y = y; self.lifespan = 120 + Math.floor(Math.random() * 60); // 2-3 seconds var scale = 0.3 + Math.random() * 0.3; graphics.scale.set(scale, scale); self.update = function () { self.y -= 0.5; self.x += Math.random() * 0.6 - 0.3; self.lifespan--; // Grow and fade var lifePercent = self.lifespan / 180; graphics.scale.set(scale * (2 - lifePercent), scale * (2 - lifePercent)); graphics.alpha = 0.3 * lifePercent; if (self.lifespan <= 0) { self.destroy(); for (var i = 0; i < smokeParticles.length; i++) { if (smokeParticles[i] === self) { smokeParticles.splice(i, 1); break; } } } }; return self; }); var Firefighter = GameObject.expand(function () { var self = GameObject.call(this, 'firefighter', 100, 150); var graphics = self.attachAsset('firefighter', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.waterLevel = 100; self.maxWaterLevel = 100; self.waterUseRate = 1; self.waterRefillRate = 3; self.isRefilling = false; self.isSprayingWater = false; self.carryingCivilian = null; self.direction = { x: 0, y: 0 }; self.update = function () { // Handle movement if (self.direction.x !== 0 || self.direction.y !== 0) { var newX = self.x + self.direction.x * self.speed; var newY = self.y + self.direction.y * self.speed; // Check for collisions with walls var wouldCollide = false; for (var i = 0; i < walls.length; i++) { var wall = walls[i]; if (willCollide(newX, newY, self.width, self.height, wall.x, wall.y, wall.width, wall.height)) { wouldCollide = true; break; } } if (!wouldCollide) { self.x = newX; self.y = newY; // Keep within game bounds self.x = Math.max(self.width / 2, Math.min(self.x, 2048 - self.width / 2)); self.y = Math.max(self.height / 2, Math.min(self.y, 2732 - self.height / 2)); // If carrying a civilian, update their position if (self.carryingCivilian) { self.carryingCivilian.x = self.x; self.carryingCivilian.y = self.y - 80; } } } // Check for water refill at fire station if (fireStation && self.intersects(fireStation)) { self.isRefilling = true; if (self.waterLevel < self.maxWaterLevel) { self.waterLevel = Math.min(self.maxWaterLevel, self.waterLevel + self.waterRefillRate); updateWaterBar(); } } else { self.isRefilling = false; } // Handle water spraying if (self.isSprayingWater && self.waterLevel > 0) { self.waterLevel = Math.max(0, self.waterLevel - self.waterUseRate); updateWaterBar(); // Create water particle createWaterParticle(self.x, self.y); } // Check exit collision when carrying civilian if (self.carryingCivilian && exit && self.intersects(exit)) { LK.getSound('rescue').play(); civiliansRescued++; self.carryingCivilian.isRescued = true; self.carryingCivilian = null; // Update the score LK.setScore(LK.getScore() + 100); updateScoreText(); // Check if all civilians are rescued checkLevelComplete(); } }; return self; }); var FireStation = GameObject.expand(function (x, y) { var self = GameObject.call(this, 'fireStation', 800, 600); var graphics = self.attachAsset('fireStation', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; return self; }); var Fire = GameObject.expand(function (x, y) { var self = GameObject.call(this, 'fire', 60, 80); var graphics = self.attachAsset('fire', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.intensity = 100; // 0-100 self.spreadTimer = 0; self.spreadRate = 5; // seconds // Randomly vary the size var scale = 0.8 + Math.random() * 0.4; graphics.scale.set(scale, scale); self.update = function () { if (self.intensity <= 0) { self.destroy(); return; } // Animate the fire if (LK.ticks % 5 === 0) { var scaleVar = Math.random() * 0.2 - 0.1; graphics.scale.set(scale + scaleVar, scale + scaleVar); } // Attempt to spread fire self.spreadTimer++; if (self.spreadTimer > self.spreadRate * 60) { // Convert seconds to frames at 60fps self.spreadTimer = 0; if (Math.random() < 0.3 && fires.length < maxFires) { var angle = Math.random() * Math.PI * 2; var distance = 100 + Math.random() * 100; var newX = self.x + Math.cos(angle) * distance; var newY = self.y + Math.sin(angle) * distance; // Keep within game bounds newX = Math.max(30, Math.min(newX, 2048 - 30)); newY = Math.max(30, Math.min(newY, 2732 - 30)); // Check if too close to existing fire or wall var tooClose = false; // Check walls for (var i = 0; i < walls.length; i++) { if (willCollide(newX, newY, 60, 80, walls[i].x, walls[i].y, walls[i].width, walls[i].height)) { tooClose = true; break; } } if (!tooClose) { createFire(newX, newY); LK.getSound('burn').play(); } } } }; return self; }); var Exit = GameObject.expand(function (x, y) { var self = GameObject.call(this, 'exit', 150, 100); var graphics = self.attachAsset('exit', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; // Make exit pulse to draw attention function pulseAnimation() { tween(graphics.scale, { x: 1.1, y: 1.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(graphics.scale, { x: 1.0, y: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: pulseAnimation }); } }); } pulseAnimation(); return self; }); var Civilian = GameObject.expand(function (x, y) { var self = GameObject.call(this, 'civilian', 80, 120); var graphics = self.attachAsset('civilian', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.isRescued = false; self.healthTimer = 0; self.maxHealthTimer = 5 * 60; // 5 seconds at 60fps self.update = function () { // Check if close to fire var nearFire = false; for (var i = 0; i < fires.length; i++) { var fire = fires[i]; var dx = self.x - fire.x; var dy = self.y - fire.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 150) { nearFire = true; break; } } if (nearFire) { // Start losing health when near fire self.healthTimer++; if (self.healthTimer >= self.maxHealthTimer) { LK.effects.flashScreen(0xff0000, 500); LK.showGameOver(); } } else { // Recover when away from fire self.healthTimer = Math.max(0, self.healthTimer - 1); } // Visual feedback on health var healthPercent = 1 - self.healthTimer / self.maxHealthTimer; graphics.alpha = 0.5 + healthPercent * 0.5; // Animate when in danger if (nearFire && LK.ticks % 10 === 0) { tween(graphics, { rotation: Math.random() * 0.2 - 0.1 }, { duration: 300 }); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 }); /**** * Game Code ****/ // Game state variables var level = storage.currentLevel || 1; var firefighter; var fires = []; var civilians = []; var walls = []; var waterParticles = []; var smokeParticles = []; var fireStation; var exit; var maxFires; var civiliansRescued = 0; var totalCivilians = 0; var gameTime = 90; // seconds var gameTimer; var controlPad; var actionButton; var timeBarFill; var waterBarFill; var joystickActive = false; var joystickOrigin = { x: 0, y: 0 }; var joystickPosition = { x: 0, y: 0 }; // Interface elements var scoreTxt; var levelTxt; var timeBarContainer; var timeBarBg; var waterBarContainer; var waterBarBg; var instructionsContainer; var instructionsTxt; // Initialize game function initGame() { // Reset game state LK.setScore(0); civiliansRescued = 0; // Clear previous game objects cleanupLevel(); // Set up the level setupLevel(level); // Create UI createUI(); // Play background music LK.playMusic('gameplay', { fade: { start: 0, end: 0.4, duration: 1000 } }); // Start game timer startGameTimer(); // Show initial instructions showInstructions(); } function cleanupLevel() { // Clear all game objects if (firefighter) { firefighter.destroy(); } for (var i = 0; i < fires.length; i++) { fires[i].destroy(); } fires = []; for (var i = 0; i < civilians.length; i++) { civilians[i].destroy(); } civilians = []; for (var i = 0; i < walls.length; i++) { walls[i].destroy(); } walls = []; for (var i = 0; i < waterParticles.length; i++) { waterParticles[i].destroy(); } waterParticles = []; for (var i = 0; i < smokeParticles.length; i++) { smokeParticles[i].destroy(); } smokeParticles = []; if (fireStation) { fireStation.destroy(); fireStation = null; } if (exit) { exit.destroy(); exit = null; } if (gameTimer) { LK.clearInterval(gameTimer); gameTimer = null; } } function setupLevel(level) { // Level configuration var levelConfig = { 1: { layout: 'basic', civilians: 2, initialFires: 3, maxFires: 6, time: 90 }, 2: { layout: 'apartment', civilians: 3, initialFires: 4, maxFires: 8, time: 120 }, 3: { layout: 'office', civilians: 4, initialFires: 5, maxFires: 10, time: 180 } }; // Use level 3 config for any level beyond 3 var config = levelConfig[level] || levelConfig[3]; config.initialFires += Math.floor((level - 3) / 2); // Increase fires for levels beyond 3 config.civilians += Math.floor((level - 3) / 2); // Increase civilians for levels beyond 3 config.time = Math.min(300, config.time + (level - 3) * 30); // Increase time (max 5 minutes) maxFires = config.maxFires; totalCivilians = config.civilians; gameTime = config.time; // Create level layout createLayout(config.layout); // Create initial fires for (var i = 0; i < config.initialFires; i++) { var fireX, fireY; var validPosition = false; // Find a valid position for the fire while (!validPosition) { fireX = 300 + Math.random() * (2048 - 600); fireY = 300 + Math.random() * (2732 - 600); // Check if too close to firefighter, fire station, or exit var tooCloseToFirefighter = distance(fireX, fireY, 200, 2500) < 300; var tooCloseToFireStation = fireStation && distance(fireX, fireY, fireStation.x, fireStation.y) < 400; var tooCloseToExit = exit && distance(fireX, fireY, exit.x, exit.y) < 400; // Check if inside any wall var insideWall = false; for (var j = 0; j < walls.length; j++) { if (willCollide(fireX, fireY, 60, 80, walls[j].x, walls[j].y, walls[j].width, walls[j].height)) { insideWall = true; break; } } validPosition = !tooCloseToFirefighter && !tooCloseToFireStation && !tooCloseToExit && !insideWall; } createFire(fireX, fireY); } // Create civilians for (var i = 0; i < config.civilians; i++) { var civX, civY; var validPosition = false; while (!validPosition) { civX = 300 + Math.random() * (2048 - 600); civY = 300 + Math.random() * (2732 - 600); // Not too close to fire station or exit var tooCloseToFireStation = fireStation && distance(civX, civY, fireStation.x, fireStation.y) < 300; var tooCloseToExit = exit && distance(civX, civY, exit.x, exit.y) < 300; // Check if inside any wall var insideWall = false; for (var j = 0; j < walls.length; j++) { if (willCollide(civX, civY, 80, 120, walls[j].x, walls[j].y, walls[j].width, walls[j].height)) { insideWall = true; break; } } // Must be somewhat close to at least one fire var nearFire = false; for (var j = 0; j < fires.length; j++) { if (distance(civX, civY, fires[j].x, fires[j].y) < 500) { nearFire = true; break; } } validPosition = !tooCloseToFireStation && !tooCloseToExit && !insideWall && nearFire; } createCivilian(civX, civY); } // Create firefighter firefighter = new Firefighter(); firefighter.x = 200; firefighter.y = 2500; game.addChild(firefighter); } function createLayout(layoutType) { // Create fire station fireStation = new FireStation(200, 2500); game.addChild(fireStation); // Create exit exit = new Exit(1800, 200); game.addChild(exit); // Create walls based on layout type switch (layoutType) { case 'basic': // Simple wall layout createWall(1024, 1366, 800, 40); // Center horizontal wall createWall(600, 1000, 40, 600); // Left vertical wall createWall(1400, 1700, 40, 600); // Right vertical wall break; case 'apartment': // Apartment-like layout createWall(300, 800, 600, 40); // Upper left horizontal createWall(1300, 800, 500, 40); // Upper right horizontal createWall(800, 800, 40, 600); // Center vertical createWall(500, 1400, 600, 40); // Lower left horizontal createWall(1300, 1400, 600, 40); // Lower right horizontal createWall(1100, 1800, 40, 800); // Lower center vertical break; case 'office': // Office building layout createWall(500, 600, 1000, 40); // Top horizontal createWall(500, 600, 40, 800); // Left vertical createWall(1500, 600, 40, 800); // Right vertical createWall(800, 1000, 600, 40); // Middle horizontal 1 createWall(1300, 1400, 400, 40); // Middle horizontal 2 createWall(1000, 1800, 1000, 40); // Bottom horizontal createWall(1000, 1400, 40, 400); // Bottom vertical break; default: // Random maze-like layout var wallCount = 6 + Math.floor(Math.random() * 6); // 6-12 walls for (var i = 0; i < wallCount; i++) { var horizontal = Math.random() > 0.5; var x, y, width, height; if (horizontal) { width = 400 + Math.random() * 800; height = 40; x = 200 + Math.random() * (2048 - 400 - width); y = 400 + Math.random() * (2732 - 800); } else { width = 40; height = 400 + Math.random() * 800; x = 400 + Math.random() * (2048 - 800); y = 200 + Math.random() * (2732 - 400 - height); } // Don't block firefighter start or exit var blocksFirefighter = willCollide(x, y, width, height, 200, 2500, 200, 200); var blocksExit = willCollide(x, y, width, height, 1800, 200, 150, 100); if (!blocksFirefighter && !blocksExit) { createWall(x, y, width, height); } } break; } } function createUI() { // Score text scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(1, 0); LK.gui.topRight.addChild(scoreTxt); // Level text levelTxt = new Text2('Level: ' + level, { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.top.addChild(levelTxt); // Time bar timeBarContainer = new Container(); timeBarBg = timeBarContainer.attachAsset('timeBarBg', { anchorX: 0, anchorY: 0.5 }); timeBarFill = timeBarContainer.attachAsset('timeBar', { anchorX: 0, anchorY: 0.5 }); timeBarFill.width = 400; // Start full timeBarContainer.addChild(timeBarBg); timeBarContainer.addChild(timeBarFill); var timeLabel = new Text2('TIME', { size: 30, fill: 0xFFFFFF }); timeLabel.anchor.set(0.5, 0.5); timeLabel.x = 200; timeBarContainer.addChild(timeLabel); timeBarContainer.x = -200; timeBarContainer.y = 100; LK.gui.top.addChild(timeBarContainer); // Water bar waterBarContainer = new Container(); waterBarBg = waterBarContainer.attachAsset('waterBarBg', { anchorX: 0, anchorY: 0.5 }); waterBarFill = waterBarContainer.attachAsset('waterBar', { anchorX: 0, anchorY: 0.5 }); waterBarFill.width = 400; // Start full waterBarContainer.addChild(waterBarBg); waterBarContainer.addChild(waterBarFill); var waterLabel = new Text2('WATER', { size: 30, fill: 0xFFFFFF }); waterLabel.anchor.set(0.5, 0.5); waterLabel.x = 200; waterBarContainer.addChild(waterLabel); waterBarContainer.x = -200; waterBarContainer.y = 160; LK.gui.top.addChild(waterBarContainer); // Instructions container instructionsContainer = new Container(); var instructionsBg = instructionsContainer.attachAsset('waterBarBg', { anchorX: 0.5, anchorY: 0.5, width: 1600, height: 400, alpha: 0.8 }); instructionsTxt = new Text2('Drag to move firefighter\nTap and hold to spray water\nRescue civilians and extinguish fires!', { size: 70, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0.5); instructionsContainer.addChild(instructionsTxt); instructionsContainer.x = 1024; instructionsContainer.y = 1366; instructionsContainer.visible = false; game.addChild(instructionsContainer); } function showInstructions() { instructionsContainer.visible = true; LK.setTimeout(function () { instructionsContainer.visible = false; }, 5000); } function startGameTimer() { var timeRemaining = gameTime; updateTimeBar(timeRemaining / gameTime); gameTimer = LK.setInterval(function () { timeRemaining--; updateTimeBar(timeRemaining / gameTime); if (timeRemaining <= 0) { LK.clearInterval(gameTimer); LK.effects.flashScreen(0xff0000, 500); LK.showGameOver(); } }, 1000); } function updateTimeBar(percentage) { timeBarFill.width = Math.max(0, 400 * percentage); // Change color as time runs out if (percentage < 0.2) { timeBarFill.tint = 0xff0000; // Red } else if (percentage < 0.5) { timeBarFill.tint = 0xffff00; // Yellow } else { timeBarFill.tint = 0xff9900; // Orange } } function updateWaterBar() { var percentage = firefighter.waterLevel / firefighter.maxWaterLevel; waterBarFill.width = Math.max(0, 400 * percentage); // Change color as water runs out if (percentage < 0.2) { waterBarFill.tint = 0xff0000; // Red } else if (percentage < 0.5) { waterBarFill.tint = 0xff9900; // Orange } else { waterBarFill.tint = 0x0000ff; // Blue } } function updateScoreText() { scoreTxt.setText('Score: ' + LK.getScore()); } function checkLevelComplete() { if (civiliansRescued >= totalCivilians) { // Level complete LK.clearInterval(gameTimer); LK.getSound('levelComplete').play(); // Award time bonus var timeRemaining = timeBarFill.width / 400 * gameTime; var timeBonus = Math.floor(timeRemaining * 10); LK.setScore(LK.getScore() + timeBonus); // Update highest level reached level++; storage.currentLevel = level; if (level > storage.highestLevel) { storage.highestLevel = level; } // Show win screen LK.showYouWin(); } } // Helper functions function createFire(x, y) { var fire = new Fire(x, y); game.addChild(fire); fires.push(fire); return fire; } function createCivilian(x, y) { var civilian = new Civilian(x, y); game.addChild(civilian); civilians.push(civilian); return civilian; } function createWall(x, y, width, height) { var wall = new Wall(x, y, width, height); game.addChild(wall); walls.push(wall); return wall; } function createWaterParticle(x, y) { // Create water spray in the direction the firefighter is facing var particleX = x + Math.random() * 40 - 20; var particleY = y - 50; // Spray from the top of the firefighter var particle = new WaterParticle(particleX, particleY); game.addChild(particle); waterParticles.push(particle); // Create smoke occasionally if (LK.ticks % 10 === 0) { createSmokeParticle(x, y); } // Play spray sound occasionally if (LK.ticks % 30 === 0) { LK.getSound('spray').play(); } return particle; } function createSmokeParticle(x, y) { var smoke = new Smoke(x + Math.random() * 60 - 30, y - 50); game.addChild(smoke); smokeParticles.push(smoke); return smoke; } function distance(x1, y1, x2, y2) { var dx = x1 - x2; var dy = y1 - y2; return Math.sqrt(dx * dx + dy * dy); } function willCollide(x1, y1, w1, h1, x2, y2, w2, h2) { return !(x1 + w1 / 2 < x2 - w2 / 2 || x1 - w1 / 2 > x2 + w2 / 2 || y1 + h1 / 2 < y2 - h2 / 2 || y1 - h1 / 2 > y2 + h2 / 2); } // Input handlers game.down = function (x, y, obj) { // Start dragging joystickActive = true; joystickOrigin.x = x; joystickOrigin.y = y; joystickPosition.x = x; joystickPosition.y = y; // If tapping close to the firefighter, start spraying water var dx = x - firefighter.x; var dy = y - firefighter.y; var distToFirefighter = Math.sqrt(dx * dx + dy * dy); if (distToFirefighter < 200) { firefighter.isSprayingWater = true; } // Check for civilian pickup if (!firefighter.carryingCivilian) { for (var i = 0; i < civilians.length; i++) { var civilian = civilians[i]; if (!civilian.isRescued && distance(firefighter.x, firefighter.y, civilian.x, civilian.y) < 100) { firefighter.carryingCivilian = civilian; LK.getSound('rescue').play(); break; } } } }; game.move = function (x, y, obj) { if (joystickActive) { joystickPosition.x = x; joystickPosition.y = y; var dx = x - joystickOrigin.x; var dy = y - joystickOrigin.y; // Calculate direction vector var length = Math.sqrt(dx * dx + dy * dy); if (length > 0) { firefighter.direction.x = dx / length; firefighter.direction.y = dy / length; } } }; game.up = function (x, y, obj) { joystickActive = false; firefighter.direction.x = 0; firefighter.direction.y = 0; firefighter.isSprayingWater = false; }; // Main game update loop game.update = function () { // Update firefighter if (firefighter) { firefighter.update(); } // Update fires for (var i = fires.length - 1; i >= 0; i--) { if (fires[i].isDestroyed) { fires.splice(i, 1); } else { fires[i].update(); } } // Update civilians for (var i = 0; i < civilians.length; i++) { if (!civilians[i].isRescued) { civilians[i].update(); } } // Update water particles for (var i = waterParticles.length - 1; i >= 0; i--) { if (waterParticles[i].isDestroyed) { waterParticles.splice(i, 1); } else { waterParticles[i].update(); } } // Update smoke particles for (var i = smokeParticles.length - 1; i >= 0; i--) { if (smokeParticles[i].isDestroyed) { smokeParticles.splice(i, 1); } else { smokeParticles[i].update(); } } // Create smoke from fires if (LK.ticks % 30 === 0) { for (var i = 0; i < fires.length; i++) { if (Math.random() < 0.3) { createSmokeParticle(fires[i].x, fires[i].y); } } } }; // Initialize the game initGame();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,894 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ currentLevel: 1,
+ highestLevel: 1
+});
+
+/****
+* Classes
+****/
+var GameObject = Container.expand(function (id, width, height) {
+ var self = Container.call(this);
+ self.id = id;
+ self.width = width;
+ self.height = height;
+ self.isDestroyed = false;
+ self.destroy = function () {
+ self.isDestroyed = true;
+ Container.prototype.destroy.call(self);
+ };
+ return self;
+});
+var WaterParticle = GameObject.expand(function (x, y) {
+ var self = GameObject.call(this, 'water', 20, 20);
+ var graphics = self.attachAsset('water', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.x = x;
+ self.y = y;
+ self.lifespan = 30; // frames
+ self.velocity = {
+ x: Math.random() * 6 - 3,
+ y: -8 - Math.random() * 3
+ };
+ self.update = function () {
+ self.x += self.velocity.x;
+ self.y += self.velocity.y;
+ self.lifespan--;
+ // Apply gravity
+ self.velocity.y += 0.3;
+ // Fade out
+ graphics.alpha = self.lifespan / 30;
+ // Check collision with fires
+ for (var i = fires.length - 1; i >= 0; i--) {
+ var fire = fires[i];
+ if (self.intersects(fire)) {
+ fire.intensity -= 5;
+ if (fire.intensity <= 0) {
+ LK.getSound('extinguish').play();
+ LK.setScore(LK.getScore() + 50);
+ updateScoreText();
+ fires.splice(i, 1);
+ fire.destroy();
+ }
+ self.lifespan = 0;
+ break;
+ }
+ }
+ if (self.lifespan <= 0) {
+ self.destroy();
+ for (var i = 0; i < waterParticles.length; i++) {
+ if (waterParticles[i] === self) {
+ waterParticles.splice(i, 1);
+ break;
+ }
+ }
+ }
+ };
+ return self;
+});
+var Wall = GameObject.expand(function (x, y, width, height) {
+ var self = GameObject.call(this, 'wall', width, height);
+ var graphics = self.attachAsset('wall', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: width,
+ height: height
+ });
+ self.x = x;
+ self.y = y;
+ return self;
+});
+var Smoke = GameObject.expand(function (x, y) {
+ var self = GameObject.call(this, 'smoke', 200, 200);
+ var graphics = self.attachAsset('smoke', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.3
+ });
+ self.x = x;
+ self.y = y;
+ self.lifespan = 120 + Math.floor(Math.random() * 60); // 2-3 seconds
+ var scale = 0.3 + Math.random() * 0.3;
+ graphics.scale.set(scale, scale);
+ self.update = function () {
+ self.y -= 0.5;
+ self.x += Math.random() * 0.6 - 0.3;
+ self.lifespan--;
+ // Grow and fade
+ var lifePercent = self.lifespan / 180;
+ graphics.scale.set(scale * (2 - lifePercent), scale * (2 - lifePercent));
+ graphics.alpha = 0.3 * lifePercent;
+ if (self.lifespan <= 0) {
+ self.destroy();
+ for (var i = 0; i < smokeParticles.length; i++) {
+ if (smokeParticles[i] === self) {
+ smokeParticles.splice(i, 1);
+ break;
+ }
+ }
+ }
+ };
+ return self;
+});
+var Firefighter = GameObject.expand(function () {
+ var self = GameObject.call(this, 'firefighter', 100, 150);
+ var graphics = self.attachAsset('firefighter', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 5;
+ self.waterLevel = 100;
+ self.maxWaterLevel = 100;
+ self.waterUseRate = 1;
+ self.waterRefillRate = 3;
+ self.isRefilling = false;
+ self.isSprayingWater = false;
+ self.carryingCivilian = null;
+ self.direction = {
+ x: 0,
+ y: 0
+ };
+ self.update = function () {
+ // Handle movement
+ if (self.direction.x !== 0 || self.direction.y !== 0) {
+ var newX = self.x + self.direction.x * self.speed;
+ var newY = self.y + self.direction.y * self.speed;
+ // Check for collisions with walls
+ var wouldCollide = false;
+ for (var i = 0; i < walls.length; i++) {
+ var wall = walls[i];
+ if (willCollide(newX, newY, self.width, self.height, wall.x, wall.y, wall.width, wall.height)) {
+ wouldCollide = true;
+ break;
+ }
+ }
+ if (!wouldCollide) {
+ self.x = newX;
+ self.y = newY;
+ // Keep within game bounds
+ self.x = Math.max(self.width / 2, Math.min(self.x, 2048 - self.width / 2));
+ self.y = Math.max(self.height / 2, Math.min(self.y, 2732 - self.height / 2));
+ // If carrying a civilian, update their position
+ if (self.carryingCivilian) {
+ self.carryingCivilian.x = self.x;
+ self.carryingCivilian.y = self.y - 80;
+ }
+ }
+ }
+ // Check for water refill at fire station
+ if (fireStation && self.intersects(fireStation)) {
+ self.isRefilling = true;
+ if (self.waterLevel < self.maxWaterLevel) {
+ self.waterLevel = Math.min(self.maxWaterLevel, self.waterLevel + self.waterRefillRate);
+ updateWaterBar();
+ }
+ } else {
+ self.isRefilling = false;
+ }
+ // Handle water spraying
+ if (self.isSprayingWater && self.waterLevel > 0) {
+ self.waterLevel = Math.max(0, self.waterLevel - self.waterUseRate);
+ updateWaterBar();
+ // Create water particle
+ createWaterParticle(self.x, self.y);
+ }
+ // Check exit collision when carrying civilian
+ if (self.carryingCivilian && exit && self.intersects(exit)) {
+ LK.getSound('rescue').play();
+ civiliansRescued++;
+ self.carryingCivilian.isRescued = true;
+ self.carryingCivilian = null;
+ // Update the score
+ LK.setScore(LK.getScore() + 100);
+ updateScoreText();
+ // Check if all civilians are rescued
+ checkLevelComplete();
+ }
+ };
+ return self;
+});
+var FireStation = GameObject.expand(function (x, y) {
+ var self = GameObject.call(this, 'fireStation', 800, 600);
+ var graphics = self.attachAsset('fireStation', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.x = x;
+ self.y = y;
+ return self;
+});
+var Fire = GameObject.expand(function (x, y) {
+ var self = GameObject.call(this, 'fire', 60, 80);
+ var graphics = self.attachAsset('fire', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.x = x;
+ self.y = y;
+ self.intensity = 100; // 0-100
+ self.spreadTimer = 0;
+ self.spreadRate = 5; // seconds
+ // Randomly vary the size
+ var scale = 0.8 + Math.random() * 0.4;
+ graphics.scale.set(scale, scale);
+ self.update = function () {
+ if (self.intensity <= 0) {
+ self.destroy();
+ return;
+ }
+ // Animate the fire
+ if (LK.ticks % 5 === 0) {
+ var scaleVar = Math.random() * 0.2 - 0.1;
+ graphics.scale.set(scale + scaleVar, scale + scaleVar);
+ }
+ // Attempt to spread fire
+ self.spreadTimer++;
+ if (self.spreadTimer > self.spreadRate * 60) {
+ // Convert seconds to frames at 60fps
+ self.spreadTimer = 0;
+ if (Math.random() < 0.3 && fires.length < maxFires) {
+ var angle = Math.random() * Math.PI * 2;
+ var distance = 100 + Math.random() * 100;
+ var newX = self.x + Math.cos(angle) * distance;
+ var newY = self.y + Math.sin(angle) * distance;
+ // Keep within game bounds
+ newX = Math.max(30, Math.min(newX, 2048 - 30));
+ newY = Math.max(30, Math.min(newY, 2732 - 30));
+ // Check if too close to existing fire or wall
+ var tooClose = false;
+ // Check walls
+ for (var i = 0; i < walls.length; i++) {
+ if (willCollide(newX, newY, 60, 80, walls[i].x, walls[i].y, walls[i].width, walls[i].height)) {
+ tooClose = true;
+ break;
+ }
+ }
+ if (!tooClose) {
+ createFire(newX, newY);
+ LK.getSound('burn').play();
+ }
+ }
+ }
+ };
+ return self;
+});
+var Exit = GameObject.expand(function (x, y) {
+ var self = GameObject.call(this, 'exit', 150, 100);
+ var graphics = self.attachAsset('exit', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.x = x;
+ self.y = y;
+ // Make exit pulse to draw attention
+ function pulseAnimation() {
+ tween(graphics.scale, {
+ x: 1.1,
+ y: 1.1
+ }, {
+ duration: 800,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(graphics.scale, {
+ x: 1.0,
+ y: 1.0
+ }, {
+ duration: 800,
+ easing: tween.easeInOut,
+ onFinish: pulseAnimation
+ });
+ }
+ });
+ }
+ pulseAnimation();
+ return self;
+});
+var Civilian = GameObject.expand(function (x, y) {
+ var self = GameObject.call(this, 'civilian', 80, 120);
+ var graphics = self.attachAsset('civilian', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.x = x;
+ self.y = y;
+ self.isRescued = false;
+ self.healthTimer = 0;
+ self.maxHealthTimer = 5 * 60; // 5 seconds at 60fps
+ self.update = function () {
+ // Check if close to fire
+ var nearFire = false;
+ for (var i = 0; i < fires.length; i++) {
+ var fire = fires[i];
+ var dx = self.x - fire.x;
+ var dy = self.y - fire.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 150) {
+ nearFire = true;
+ break;
+ }
+ }
+ if (nearFire) {
+ // Start losing health when near fire
+ self.healthTimer++;
+ if (self.healthTimer >= self.maxHealthTimer) {
+ LK.effects.flashScreen(0xff0000, 500);
+ LK.showGameOver();
+ }
+ } else {
+ // Recover when away from fire
+ self.healthTimer = Math.max(0, self.healthTimer - 1);
+ }
+ // Visual feedback on health
+ var healthPercent = 1 - self.healthTimer / self.maxHealthTimer;
+ graphics.alpha = 0.5 + healthPercent * 0.5;
+ // Animate when in danger
+ if (nearFire && LK.ticks % 10 === 0) {
+ tween(graphics, {
+ rotation: Math.random() * 0.2 - 0.1
+ }, {
+ duration: 300
+ });
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x333333
+});
+
+/****
+* Game Code
+****/
+// Game state variables
+var level = storage.currentLevel || 1;
+var firefighter;
+var fires = [];
+var civilians = [];
+var walls = [];
+var waterParticles = [];
+var smokeParticles = [];
+var fireStation;
+var exit;
+var maxFires;
+var civiliansRescued = 0;
+var totalCivilians = 0;
+var gameTime = 90; // seconds
+var gameTimer;
+var controlPad;
+var actionButton;
+var timeBarFill;
+var waterBarFill;
+var joystickActive = false;
+var joystickOrigin = {
+ x: 0,
+ y: 0
+};
+var joystickPosition = {
+ x: 0,
+ y: 0
+};
+// Interface elements
+var scoreTxt;
+var levelTxt;
+var timeBarContainer;
+var timeBarBg;
+var waterBarContainer;
+var waterBarBg;
+var instructionsContainer;
+var instructionsTxt;
+// Initialize game
+function initGame() {
+ // Reset game state
+ LK.setScore(0);
+ civiliansRescued = 0;
+ // Clear previous game objects
+ cleanupLevel();
+ // Set up the level
+ setupLevel(level);
+ // Create UI
+ createUI();
+ // Play background music
+ LK.playMusic('gameplay', {
+ fade: {
+ start: 0,
+ end: 0.4,
+ duration: 1000
+ }
+ });
+ // Start game timer
+ startGameTimer();
+ // Show initial instructions
+ showInstructions();
+}
+function cleanupLevel() {
+ // Clear all game objects
+ if (firefighter) {
+ firefighter.destroy();
+ }
+ for (var i = 0; i < fires.length; i++) {
+ fires[i].destroy();
+ }
+ fires = [];
+ for (var i = 0; i < civilians.length; i++) {
+ civilians[i].destroy();
+ }
+ civilians = [];
+ for (var i = 0; i < walls.length; i++) {
+ walls[i].destroy();
+ }
+ walls = [];
+ for (var i = 0; i < waterParticles.length; i++) {
+ waterParticles[i].destroy();
+ }
+ waterParticles = [];
+ for (var i = 0; i < smokeParticles.length; i++) {
+ smokeParticles[i].destroy();
+ }
+ smokeParticles = [];
+ if (fireStation) {
+ fireStation.destroy();
+ fireStation = null;
+ }
+ if (exit) {
+ exit.destroy();
+ exit = null;
+ }
+ if (gameTimer) {
+ LK.clearInterval(gameTimer);
+ gameTimer = null;
+ }
+}
+function setupLevel(level) {
+ // Level configuration
+ var levelConfig = {
+ 1: {
+ layout: 'basic',
+ civilians: 2,
+ initialFires: 3,
+ maxFires: 6,
+ time: 90
+ },
+ 2: {
+ layout: 'apartment',
+ civilians: 3,
+ initialFires: 4,
+ maxFires: 8,
+ time: 120
+ },
+ 3: {
+ layout: 'office',
+ civilians: 4,
+ initialFires: 5,
+ maxFires: 10,
+ time: 180
+ }
+ };
+ // Use level 3 config for any level beyond 3
+ var config = levelConfig[level] || levelConfig[3];
+ config.initialFires += Math.floor((level - 3) / 2); // Increase fires for levels beyond 3
+ config.civilians += Math.floor((level - 3) / 2); // Increase civilians for levels beyond 3
+ config.time = Math.min(300, config.time + (level - 3) * 30); // Increase time (max 5 minutes)
+ maxFires = config.maxFires;
+ totalCivilians = config.civilians;
+ gameTime = config.time;
+ // Create level layout
+ createLayout(config.layout);
+ // Create initial fires
+ for (var i = 0; i < config.initialFires; i++) {
+ var fireX, fireY;
+ var validPosition = false;
+ // Find a valid position for the fire
+ while (!validPosition) {
+ fireX = 300 + Math.random() * (2048 - 600);
+ fireY = 300 + Math.random() * (2732 - 600);
+ // Check if too close to firefighter, fire station, or exit
+ var tooCloseToFirefighter = distance(fireX, fireY, 200, 2500) < 300;
+ var tooCloseToFireStation = fireStation && distance(fireX, fireY, fireStation.x, fireStation.y) < 400;
+ var tooCloseToExit = exit && distance(fireX, fireY, exit.x, exit.y) < 400;
+ // Check if inside any wall
+ var insideWall = false;
+ for (var j = 0; j < walls.length; j++) {
+ if (willCollide(fireX, fireY, 60, 80, walls[j].x, walls[j].y, walls[j].width, walls[j].height)) {
+ insideWall = true;
+ break;
+ }
+ }
+ validPosition = !tooCloseToFirefighter && !tooCloseToFireStation && !tooCloseToExit && !insideWall;
+ }
+ createFire(fireX, fireY);
+ }
+ // Create civilians
+ for (var i = 0; i < config.civilians; i++) {
+ var civX, civY;
+ var validPosition = false;
+ while (!validPosition) {
+ civX = 300 + Math.random() * (2048 - 600);
+ civY = 300 + Math.random() * (2732 - 600);
+ // Not too close to fire station or exit
+ var tooCloseToFireStation = fireStation && distance(civX, civY, fireStation.x, fireStation.y) < 300;
+ var tooCloseToExit = exit && distance(civX, civY, exit.x, exit.y) < 300;
+ // Check if inside any wall
+ var insideWall = false;
+ for (var j = 0; j < walls.length; j++) {
+ if (willCollide(civX, civY, 80, 120, walls[j].x, walls[j].y, walls[j].width, walls[j].height)) {
+ insideWall = true;
+ break;
+ }
+ }
+ // Must be somewhat close to at least one fire
+ var nearFire = false;
+ for (var j = 0; j < fires.length; j++) {
+ if (distance(civX, civY, fires[j].x, fires[j].y) < 500) {
+ nearFire = true;
+ break;
+ }
+ }
+ validPosition = !tooCloseToFireStation && !tooCloseToExit && !insideWall && nearFire;
+ }
+ createCivilian(civX, civY);
+ }
+ // Create firefighter
+ firefighter = new Firefighter();
+ firefighter.x = 200;
+ firefighter.y = 2500;
+ game.addChild(firefighter);
+}
+function createLayout(layoutType) {
+ // Create fire station
+ fireStation = new FireStation(200, 2500);
+ game.addChild(fireStation);
+ // Create exit
+ exit = new Exit(1800, 200);
+ game.addChild(exit);
+ // Create walls based on layout type
+ switch (layoutType) {
+ case 'basic':
+ // Simple wall layout
+ createWall(1024, 1366, 800, 40); // Center horizontal wall
+ createWall(600, 1000, 40, 600); // Left vertical wall
+ createWall(1400, 1700, 40, 600); // Right vertical wall
+ break;
+ case 'apartment':
+ // Apartment-like layout
+ createWall(300, 800, 600, 40); // Upper left horizontal
+ createWall(1300, 800, 500, 40); // Upper right horizontal
+ createWall(800, 800, 40, 600); // Center vertical
+ createWall(500, 1400, 600, 40); // Lower left horizontal
+ createWall(1300, 1400, 600, 40); // Lower right horizontal
+ createWall(1100, 1800, 40, 800); // Lower center vertical
+ break;
+ case 'office':
+ // Office building layout
+ createWall(500, 600, 1000, 40); // Top horizontal
+ createWall(500, 600, 40, 800); // Left vertical
+ createWall(1500, 600, 40, 800); // Right vertical
+ createWall(800, 1000, 600, 40); // Middle horizontal 1
+ createWall(1300, 1400, 400, 40); // Middle horizontal 2
+ createWall(1000, 1800, 1000, 40); // Bottom horizontal
+ createWall(1000, 1400, 40, 400); // Bottom vertical
+ break;
+ default:
+ // Random maze-like layout
+ var wallCount = 6 + Math.floor(Math.random() * 6); // 6-12 walls
+ for (var i = 0; i < wallCount; i++) {
+ var horizontal = Math.random() > 0.5;
+ var x, y, width, height;
+ if (horizontal) {
+ width = 400 + Math.random() * 800;
+ height = 40;
+ x = 200 + Math.random() * (2048 - 400 - width);
+ y = 400 + Math.random() * (2732 - 800);
+ } else {
+ width = 40;
+ height = 400 + Math.random() * 800;
+ x = 400 + Math.random() * (2048 - 800);
+ y = 200 + Math.random() * (2732 - 400 - height);
+ }
+ // Don't block firefighter start or exit
+ var blocksFirefighter = willCollide(x, y, width, height, 200, 2500, 200, 200);
+ var blocksExit = willCollide(x, y, width, height, 1800, 200, 150, 100);
+ if (!blocksFirefighter && !blocksExit) {
+ createWall(x, y, width, height);
+ }
+ }
+ break;
+ }
+}
+function createUI() {
+ // Score text
+ scoreTxt = new Text2('Score: 0', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ scoreTxt.anchor.set(1, 0);
+ LK.gui.topRight.addChild(scoreTxt);
+ // Level text
+ levelTxt = new Text2('Level: ' + level, {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ levelTxt.anchor.set(0, 0);
+ LK.gui.top.addChild(levelTxt);
+ // Time bar
+ timeBarContainer = new Container();
+ timeBarBg = timeBarContainer.attachAsset('timeBarBg', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ timeBarFill = timeBarContainer.attachAsset('timeBar', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ timeBarFill.width = 400; // Start full
+ timeBarContainer.addChild(timeBarBg);
+ timeBarContainer.addChild(timeBarFill);
+ var timeLabel = new Text2('TIME', {
+ size: 30,
+ fill: 0xFFFFFF
+ });
+ timeLabel.anchor.set(0.5, 0.5);
+ timeLabel.x = 200;
+ timeBarContainer.addChild(timeLabel);
+ timeBarContainer.x = -200;
+ timeBarContainer.y = 100;
+ LK.gui.top.addChild(timeBarContainer);
+ // Water bar
+ waterBarContainer = new Container();
+ waterBarBg = waterBarContainer.attachAsset('waterBarBg', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ waterBarFill = waterBarContainer.attachAsset('waterBar', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ waterBarFill.width = 400; // Start full
+ waterBarContainer.addChild(waterBarBg);
+ waterBarContainer.addChild(waterBarFill);
+ var waterLabel = new Text2('WATER', {
+ size: 30,
+ fill: 0xFFFFFF
+ });
+ waterLabel.anchor.set(0.5, 0.5);
+ waterLabel.x = 200;
+ waterBarContainer.addChild(waterLabel);
+ waterBarContainer.x = -200;
+ waterBarContainer.y = 160;
+ LK.gui.top.addChild(waterBarContainer);
+ // Instructions container
+ instructionsContainer = new Container();
+ var instructionsBg = instructionsContainer.attachAsset('waterBarBg', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 1600,
+ height: 400,
+ alpha: 0.8
+ });
+ instructionsTxt = new Text2('Drag to move firefighter\nTap and hold to spray water\nRescue civilians and extinguish fires!', {
+ size: 70,
+ fill: 0xFFFFFF
+ });
+ instructionsTxt.anchor.set(0.5, 0.5);
+ instructionsContainer.addChild(instructionsTxt);
+ instructionsContainer.x = 1024;
+ instructionsContainer.y = 1366;
+ instructionsContainer.visible = false;
+ game.addChild(instructionsContainer);
+}
+function showInstructions() {
+ instructionsContainer.visible = true;
+ LK.setTimeout(function () {
+ instructionsContainer.visible = false;
+ }, 5000);
+}
+function startGameTimer() {
+ var timeRemaining = gameTime;
+ updateTimeBar(timeRemaining / gameTime);
+ gameTimer = LK.setInterval(function () {
+ timeRemaining--;
+ updateTimeBar(timeRemaining / gameTime);
+ if (timeRemaining <= 0) {
+ LK.clearInterval(gameTimer);
+ LK.effects.flashScreen(0xff0000, 500);
+ LK.showGameOver();
+ }
+ }, 1000);
+}
+function updateTimeBar(percentage) {
+ timeBarFill.width = Math.max(0, 400 * percentage);
+ // Change color as time runs out
+ if (percentage < 0.2) {
+ timeBarFill.tint = 0xff0000; // Red
+ } else if (percentage < 0.5) {
+ timeBarFill.tint = 0xffff00; // Yellow
+ } else {
+ timeBarFill.tint = 0xff9900; // Orange
+ }
+}
+function updateWaterBar() {
+ var percentage = firefighter.waterLevel / firefighter.maxWaterLevel;
+ waterBarFill.width = Math.max(0, 400 * percentage);
+ // Change color as water runs out
+ if (percentage < 0.2) {
+ waterBarFill.tint = 0xff0000; // Red
+ } else if (percentage < 0.5) {
+ waterBarFill.tint = 0xff9900; // Orange
+ } else {
+ waterBarFill.tint = 0x0000ff; // Blue
+ }
+}
+function updateScoreText() {
+ scoreTxt.setText('Score: ' + LK.getScore());
+}
+function checkLevelComplete() {
+ if (civiliansRescued >= totalCivilians) {
+ // Level complete
+ LK.clearInterval(gameTimer);
+ LK.getSound('levelComplete').play();
+ // Award time bonus
+ var timeRemaining = timeBarFill.width / 400 * gameTime;
+ var timeBonus = Math.floor(timeRemaining * 10);
+ LK.setScore(LK.getScore() + timeBonus);
+ // Update highest level reached
+ level++;
+ storage.currentLevel = level;
+ if (level > storage.highestLevel) {
+ storage.highestLevel = level;
+ }
+ // Show win screen
+ LK.showYouWin();
+ }
+}
+// Helper functions
+function createFire(x, y) {
+ var fire = new Fire(x, y);
+ game.addChild(fire);
+ fires.push(fire);
+ return fire;
+}
+function createCivilian(x, y) {
+ var civilian = new Civilian(x, y);
+ game.addChild(civilian);
+ civilians.push(civilian);
+ return civilian;
+}
+function createWall(x, y, width, height) {
+ var wall = new Wall(x, y, width, height);
+ game.addChild(wall);
+ walls.push(wall);
+ return wall;
+}
+function createWaterParticle(x, y) {
+ // Create water spray in the direction the firefighter is facing
+ var particleX = x + Math.random() * 40 - 20;
+ var particleY = y - 50; // Spray from the top of the firefighter
+ var particle = new WaterParticle(particleX, particleY);
+ game.addChild(particle);
+ waterParticles.push(particle);
+ // Create smoke occasionally
+ if (LK.ticks % 10 === 0) {
+ createSmokeParticle(x, y);
+ }
+ // Play spray sound occasionally
+ if (LK.ticks % 30 === 0) {
+ LK.getSound('spray').play();
+ }
+ return particle;
+}
+function createSmokeParticle(x, y) {
+ var smoke = new Smoke(x + Math.random() * 60 - 30, y - 50);
+ game.addChild(smoke);
+ smokeParticles.push(smoke);
+ return smoke;
+}
+function distance(x1, y1, x2, y2) {
+ var dx = x1 - x2;
+ var dy = y1 - y2;
+ return Math.sqrt(dx * dx + dy * dy);
+}
+function willCollide(x1, y1, w1, h1, x2, y2, w2, h2) {
+ return !(x1 + w1 / 2 < x2 - w2 / 2 || x1 - w1 / 2 > x2 + w2 / 2 || y1 + h1 / 2 < y2 - h2 / 2 || y1 - h1 / 2 > y2 + h2 / 2);
+}
+// Input handlers
+game.down = function (x, y, obj) {
+ // Start dragging
+ joystickActive = true;
+ joystickOrigin.x = x;
+ joystickOrigin.y = y;
+ joystickPosition.x = x;
+ joystickPosition.y = y;
+ // If tapping close to the firefighter, start spraying water
+ var dx = x - firefighter.x;
+ var dy = y - firefighter.y;
+ var distToFirefighter = Math.sqrt(dx * dx + dy * dy);
+ if (distToFirefighter < 200) {
+ firefighter.isSprayingWater = true;
+ }
+ // Check for civilian pickup
+ if (!firefighter.carryingCivilian) {
+ for (var i = 0; i < civilians.length; i++) {
+ var civilian = civilians[i];
+ if (!civilian.isRescued && distance(firefighter.x, firefighter.y, civilian.x, civilian.y) < 100) {
+ firefighter.carryingCivilian = civilian;
+ LK.getSound('rescue').play();
+ break;
+ }
+ }
+ }
+};
+game.move = function (x, y, obj) {
+ if (joystickActive) {
+ joystickPosition.x = x;
+ joystickPosition.y = y;
+ var dx = x - joystickOrigin.x;
+ var dy = y - joystickOrigin.y;
+ // Calculate direction vector
+ var length = Math.sqrt(dx * dx + dy * dy);
+ if (length > 0) {
+ firefighter.direction.x = dx / length;
+ firefighter.direction.y = dy / length;
+ }
+ }
+};
+game.up = function (x, y, obj) {
+ joystickActive = false;
+ firefighter.direction.x = 0;
+ firefighter.direction.y = 0;
+ firefighter.isSprayingWater = false;
+};
+// Main game update loop
+game.update = function () {
+ // Update firefighter
+ if (firefighter) {
+ firefighter.update();
+ }
+ // Update fires
+ for (var i = fires.length - 1; i >= 0; i--) {
+ if (fires[i].isDestroyed) {
+ fires.splice(i, 1);
+ } else {
+ fires[i].update();
+ }
+ }
+ // Update civilians
+ for (var i = 0; i < civilians.length; i++) {
+ if (!civilians[i].isRescued) {
+ civilians[i].update();
+ }
+ }
+ // Update water particles
+ for (var i = waterParticles.length - 1; i >= 0; i--) {
+ if (waterParticles[i].isDestroyed) {
+ waterParticles.splice(i, 1);
+ } else {
+ waterParticles[i].update();
+ }
+ }
+ // Update smoke particles
+ for (var i = smokeParticles.length - 1; i >= 0; i--) {
+ if (smokeParticles[i].isDestroyed) {
+ smokeParticles.splice(i, 1);
+ } else {
+ smokeParticles[i].update();
+ }
+ }
+ // Create smoke from fires
+ if (LK.ticks % 30 === 0) {
+ for (var i = 0; i < fires.length; i++) {
+ if (Math.random() < 0.3) {
+ createSmokeParticle(fires[i].x, fires[i].y);
+ }
+ }
+ }
+};
+// Initialize the game
+initGame();
\ No newline at end of file
full size civilian Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
fire. Single Game Texture. In-Game asset. 2d. Blank background. No shadows
firestation. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
horizontal wall. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
smoke. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
water drop Single Game Texture. In-Game asset. 2d. Blank background. No shadows
firefighter using fire extinguisher Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Exit Gate. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows