User prompt
create and assign the background music for the game
User prompt
convert the environment to maze
User prompt
set minimum score points at each level to pass\
User prompt
convert the environment to maze
User prompt
fix the game over bug that popup automatically
User prompt
make the game more challenging
User prompt
make the player input like first we have to hold and draw the mouse input on that path which will be shown on the screen after the input is complete then player has to move only on that way ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
optimize the mouse hold and drag input smoother
User prompt
fix the game over bug
User prompt
fix the game over bug
User prompt
create and assign some of the assets near the wall
User prompt
add a building asset for each wall so it will look like a house
User prompt
add a health bar to the firefighter if he directly collide with fire reduce the health with little
User prompt
scale the exit gate asset liitle bit bigger ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add building assets in the game to make it more realistic
User prompt
Please fix the bug: 'Timeout.tick error: distance is not a function' in or related to this line: 'var tooCloseToFirefighter = distance(pickupX, pickupY, firefighter.x, firefighter.y) < 200;' Line Number: 956
User prompt
Please fix the bug: 'distance is not a function' in or related to this line: 'var tooCloseToFirefighter = distance(pickupX, pickupY, firefighter.x, firefighter.y) < 200;' Line Number: 950
User prompt
Please fix the bug: 'distance is not a function' in or related to this line: 'var tooCloseToFirefighter = distance(pickupX, pickupY, firefighter.x, firefighter.y) < 200;' Line Number: 944
User prompt
Please fix the bug: 'distance is not a function' in or related to this line: 'var tooCloseToFirefighter = distance(pickupX, pickupY, firefighter.x, firefighter.y) < 200;' Line Number: 942
User prompt
add more water pickups across the game so the firefighter can pickup
User prompt
reduce the water reducing amount which gets emptier quickly
User prompt
spawn those across the game that'll help the firefighter to refill the status
User prompt
add water pickup to collect refill the water status
User prompt
dont squash the walls
User prompt
make the walls in horizontal
/****
* 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 WaterPickup = GameObject.expand(function (x, y) {
var self = GameObject.call(this, 'waterPickup', 60, 60);
var graphics = self.attachAsset('water', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
self.x = x;
self.y = y;
self.waterAmount = 75; // Increased water amount
// Make water pickup pulse to draw attention
function pulseAnimation() {
tween(graphics.scale, {
x: 3.3,
y: 3.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphics.scale, {
x: 3.0,
y: 3.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: pulseAnimation
});
}
});
}
pulseAnimation();
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
});
// Add health bar
var healthBarBg = self.attachAsset('timeBarBg', {
anchorX: 0.5,
anchorY: 0,
width: 100,
height: 10,
y: -90
});
var healthBarFill = self.attachAsset('timeBar', {
anchorX: 0.5,
anchorY: 0,
width: 100,
height: 10,
y: -90,
tint: 0x00ff00
});
self.speed = 5;
self.waterLevel = 100;
self.maxWaterLevel = 100;
self.waterUseRate = 0.5;
self.waterRefillRate = 1.5; // was 3, now slower refill
self.health = 100;
self.maxHealth = 100;
self.damageTimer = 0;
self.damageDelay = 30; // 0.5 seconds at 60fps
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();
}
// Also heal when at fire station
if (self.health < self.maxHealth) {
self.health = Math.min(self.maxHealth, self.health + 0.2); // was 0.5, now slower recovery
healthBarFill.width = self.health / self.maxHealth * 100;
// Update color based on health
if (self.health < 30) {
healthBarFill.tint = 0xff0000; // Red
} else if (self.health < 60) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0x00ff00; // Green
}
}
} else {
self.isRefilling = false;
}
// Check for water pickup collision
for (var i = waterPickups.length - 1; i >= 0; i--) {
if (self.intersects(waterPickups[i])) {
// Refill water
self.waterLevel = Math.min(self.maxWaterLevel, self.waterLevel + waterPickups[i].waterAmount);
updateWaterBar();
// Add score
LK.setScore(LK.getScore() + 25);
updateScoreText();
// Show pickup effect
LK.effects.flashObject(self, 0x0000ff, 500);
// Create water particle burst effect
for (var j = 0; j < 8; j++) {
var angle = j * Math.PI / 4;
var particleX = self.x + Math.cos(angle) * 50;
var particleY = self.y + Math.sin(angle) * 50;
var particle = new WaterParticle(particleX, particleY);
particle.velocity.x = Math.cos(angle) * 4;
particle.velocity.y = Math.sin(angle) * 4;
game.addChild(particle);
waterParticles.push(particle);
}
// Play sound
LK.getSound('spray').play();
// Remove pickup
waterPickups[i].destroy();
waterPickups.splice(i, 1);
}
}
// 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 for fire collisions and take damage
if (self.damageTimer <= 0) {
for (var i = 0; i < fires.length; i++) {
if (self.intersects(fires[i])) {
self.health -= 10;
self.damageTimer = self.damageDelay;
// Update health bar
healthBarFill.width = self.health / self.maxHealth * 100;
// Change color based on health
if (self.health < 30) {
healthBarFill.tint = 0xff0000; // Red
} else if (self.health < 60) {
healthBarFill.tint = 0xffff00; // Yellow
}
// Visual feedback
LK.effects.flashObject(self, 0xff0000, 300);
// Game over if health depleted
if (self.health <= 0 && !self.isDestroyed) {
self.isDestroyed = true;
LK.effects.flashScreen(0xff0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
break;
}
}
} else {
self.damageTimer--;
}
// 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 = 3; // seconds (was 5, now faster)
// 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.5 && fires.length < maxFires) {
// was 0.3, now 0.5 (more likely to spread)
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.3,
y: 1.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphics.scale, {
x: 1.2,
y: 1.2
}, {
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 && !self.isRescued && !self.isDestroyed) {
self.isDestroyed = true;
LK.effects.flashScreen(0xff0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
} 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 waterPickups = [];
var waterPickupTimer;
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', {
loop: true,
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
// Start game timer
startGameTimer();
// Show initial instructions
showInstructions();
// Start water pickup spawning
if (waterPickupTimer) {
LK.clearInterval(waterPickupTimer);
}
// Create multiple initial water pickups based on level
var initialPickups = 2 + Math.floor(level / 2);
for (var i = 0; i < initialPickups; i++) {
createWaterPickup();
}
// Spawn new water pickups periodically
waterPickupTimer = LK.setInterval(function () {
// Limit the number of pickups based on level
if (waterPickups.length < 3 + Math.floor(level)) {
createWaterPickup();
}
}, 25000); // New water pickup every 25 seconds (was 15s, now less frequent)
}
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 = [];
for (var i = 0; i < waterPickups.length; i++) {
waterPickups[i].destroy();
}
waterPickups = [];
if (waterPickupTimer) {
LK.clearInterval(waterPickupTimer);
waterPickupTimer = null;
}
if (fireStation) {
fireStation.destroy();
fireStation = null;
}
if (exit) {
exit.destroy();
exit = null;
}
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
}
function setupLevel(level) {
// Define distance function first so it can be used in this method
function distance(x1, y1, x2, y2) {
var dx = x1 - x2;
var dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
// 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 dx1 = fireX - 200;
var dy1 = fireY - 2500;
var tooCloseToFirefighter = Math.sqrt(dx1 * dx1 + dy1 * dy1) < 300;
var tooCloseToFireStation = fireStation && Math.sqrt(Math.pow(fireX - fireStation.x, 2) + Math.pow(fireY - fireStation.y, 2)) < 400;
var tooCloseToExit = exit && Math.sqrt(Math.pow(fireX - exit.x, 2) + Math.pow(fireY - exit.y, 2)) < 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, 50); // Center horizontal wall
createWall(600, 1000, 600, 50); // Left horizontal wall
createWall(1400, 1700, 600, 50); // Right horizontal wall
break;
case 'apartment':
// Apartment-like layout
createWall(300, 800, 600, 50); // Upper left horizontal
createWall(1300, 800, 500, 50); // Upper right horizontal
createWall(500, 1400, 600, 50); // Lower left horizontal
createWall(1300, 1400, 600, 50); // Lower right horizontal
createWall(800, 800, 40, 600); // Center vertical
createWall(1100, 1800, 40, 800); // Lower center vertical
break;
case 'office':
// Office building layout
createWall(500, 600, 1000, 50); // Top horizontal
createWall(800, 1000, 600, 50); // Middle horizontal 1
createWall(1300, 1400, 400, 50); // Middle horizontal 2
createWall(1000, 1800, 1000, 50); // Bottom horizontal
createWall(500, 600, 40, 800); // Left vertical
createWall(1500, 600, 40, 800); // Right vertical
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 createWaterPickup() {
// Define distance function locally to ensure it's available
function distance(x1, y1, x2, y2) {
var dx = x1 - x2;
var dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
// Find a valid position for the water pickup
var pickupX, pickupY;
var validPosition = false;
var preferredPlacement = Math.random();
while (!validPosition) {
// Try to place water pickups strategically
if (fires.length > 0 && preferredPlacement < 0.5) {
// Place near fires (but not too close)
var randomFire = fires[Math.floor(Math.random() * fires.length)];
var angle = Math.random() * Math.PI * 2;
var dist = 250 + Math.random() * 150;
pickupX = randomFire.x + Math.cos(angle) * dist;
pickupY = randomFire.y + Math.sin(angle) * dist;
} else if (civilians.length > 0 && preferredPlacement >= 0.5 && preferredPlacement < 0.8) {
// Place near civilians that need rescue
var unrescuedCivilians = civilians.filter(function (civ) {
return !civ.isRescued;
});
if (unrescuedCivilians.length > 0) {
var randomCivilian = unrescuedCivilians[Math.floor(Math.random() * unrescuedCivilians.length)];
var angle = Math.random() * Math.PI * 2;
var dist = 200 + Math.random() * 100;
pickupX = randomCivilian.x + Math.cos(angle) * dist;
pickupY = randomCivilian.y + Math.sin(angle) * dist;
} else {
pickupX = 300 + Math.random() * (2048 - 600);
pickupY = 300 + Math.random() * (2732 - 600);
}
} else {
// Random placement
pickupX = 300 + Math.random() * (2048 - 600);
pickupY = 300 + Math.random() * (2732 - 600);
}
// Keep within game bounds
pickupX = Math.max(100, Math.min(pickupX, 2048 - 100));
pickupY = Math.max(100, Math.min(pickupY, 2732 - 100));
// Check if too close to firefighter or other objects
var tooCloseToFirefighter = distance(pickupX, pickupY, firefighter.x, firefighter.y) < 200;
var tooCloseToFireStation = fireStation && distance(pickupX, pickupY, fireStation.x, fireStation.y) < 300;
var tooCloseToExit = exit && distance(pickupX, pickupY, exit.x, exit.y) < 300;
// Check if inside any wall
var insideWall = false;
for (var j = 0; j < walls.length; j++) {
if (willCollide(pickupX, pickupY, 60, 60, walls[j].x, walls[j].y, walls[j].width, walls[j].height)) {
insideWall = true;
break;
}
}
// Check if not too close to existing pickups
var tooCloseToPickup = false;
for (var j = 0; j < waterPickups.length; j++) {
if (distance(pickupX, pickupY, waterPickups[j].x, waterPickups[j].y) < 300) {
tooCloseToPickup = true;
break;
}
}
validPosition = !tooCloseToFirefighter && !tooCloseToFireStation && !tooCloseToExit && !insideWall && !tooCloseToPickup;
}
var waterPickup = new WaterPickup(pickupX, pickupY);
game.addChild(waterPickup);
waterPickups.push(waterPickup);
return waterPickup;
}
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
@@ -104,20 +104,18 @@
}
};
return self;
});
-var Wall = GameObject.expand(function (x, y, width, height, wallType) {
+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,
- tint: wallType === 'border' ? 0x666666 : wallType === 'inner' ? 0x888888 : 0x444444
+ height: height
});
self.x = x;
self.y = y;
- self.wallType = wallType || 'normal';
return self;
});
var Smoke = GameObject.expand(function (x, y) {
var self = GameObject.call(this, 'smoke', 200, 200);
@@ -531,8 +529,9 @@
// Create UI
createUI();
// Play background music
LK.playMusic('gameplay', {
+ loop: true,
fade: {
start: 0,
end: 0.4,
duration: 1000
@@ -614,27 +613,27 @@
}
// Level configuration
var levelConfig = {
1: {
- layout: 'maze',
+ layout: 'basic',
civilians: 2,
initialFires: 3,
maxFires: 6,
- time: 120
+ time: 90
},
2: {
- layout: 'complex_maze',
+ layout: 'apartment',
civilians: 3,
initialFires: 4,
maxFires: 8,
- time: 150
+ time: 120
},
3: {
- layout: 'labyrinth',
+ layout: 'office',
civilians: 4,
initialFires: 5,
maxFires: 10,
- time: 210
+ time: 180
}
};
// Use level 3 config for any level beyond 3
var config = levelConfig[level] || levelConfig[3];
@@ -716,164 +715,60 @@
exit = new Exit(1800, 200);
game.addChild(exit);
// Create walls based on layout type
switch (layoutType) {
- case 'maze':
- // Create a simple maze layout
- createMazeGrid(10, 12, 60, false);
+ case 'basic':
+ // Simple wall layout
+ createWall(1024, 1366, 800, 50); // Center horizontal wall
+ createWall(600, 1000, 600, 50); // Left horizontal wall
+ createWall(1400, 1700, 600, 50); // Right horizontal wall
break;
- case 'complex_maze':
- // Create a more complex maze
- createMazeGrid(12, 15, 70, true);
+ case 'apartment':
+ // Apartment-like layout
+ createWall(300, 800, 600, 50); // Upper left horizontal
+ createWall(1300, 800, 500, 50); // Upper right horizontal
+ createWall(500, 1400, 600, 50); // Lower left horizontal
+ createWall(1300, 1400, 600, 50); // Lower right horizontal
+ createWall(800, 800, 40, 600); // Center vertical
+ createWall(1100, 1800, 40, 800); // Lower center vertical
break;
- case 'labyrinth':
- // Create a labyrinth with more complex paths
- createMazeGrid(15, 18, 80, true);
+ case 'office':
+ // Office building layout
+ createWall(500, 600, 1000, 50); // Top horizontal
+ createWall(800, 1000, 600, 50); // Middle horizontal 1
+ createWall(1300, 1400, 400, 50); // Middle horizontal 2
+ createWall(1000, 1800, 1000, 50); // Bottom horizontal
+ createWall(500, 600, 40, 800); // Left vertical
+ createWall(1500, 600, 40, 800); // Right vertical
+ createWall(1000, 1400, 40, 400); // Bottom vertical
break;
default:
// Random maze-like layout
- createMazeGrid(8, 10, 50, false);
- break;
- }
- // Create game boundary walls
- var wallThickness = 60;
- // Top wall
- createWall(1024, wallThickness / 2, 2048, wallThickness, 'border');
- // Bottom wall
- createWall(1024, 2732 - wallThickness / 2, 2048, wallThickness, 'border');
- // Left wall
- createWall(wallThickness / 2, 1366, wallThickness, 2732, 'border');
- // Right wall
- createWall(2048 - wallThickness / 2, 1366, wallThickness, 2732, 'border');
- // Create clear paths to fire station and exit
- clearPathToPoint(200, 2500, 300); // Clear path to fire station
- clearPathToPoint(1800, 200, 300); // Clear path to exit
-}
-// Helper function to create a maze grid
-function createMazeGrid(cols, rows, wallThickness, complexPaths) {
- var cellWidth = (2048 - 2 * wallThickness) / cols;
- var cellHeight = (2732 - 2 * wallThickness) / rows;
- var grid = [];
- // Initialize grid
- for (var i = 0; i < rows; i++) {
- grid[i] = [];
- for (var j = 0; j < cols; j++) {
- grid[i][j] = {
- visited: false,
- walls: {
- top: true,
- right: true,
- bottom: true,
- left: true
+ 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);
}
- };
- }
- }
- // Use depth-first search to generate maze
- function generateMaze(row, col) {
- grid[row][col].visited = true;
- // Define possible directions
- var directions = [{
- row: -1,
- col: 0,
- wall: 'top',
- opposite: 'bottom'
- },
- // Top
- {
- row: 0,
- col: 1,
- wall: 'right',
- opposite: 'left'
- },
- // Right
- {
- row: 1,
- col: 0,
- wall: 'bottom',
- opposite: 'top'
- },
- // Bottom
- {
- row: 0,
- col: -1,
- wall: 'left',
- opposite: 'right'
- } // Left
- ];
- // Shuffle directions for randomness
- for (var i = directions.length - 1; i > 0; i--) {
- var j = Math.floor(Math.random() * (i + 1));
- var temp = directions[i];
- directions[i] = directions[j];
- directions[j] = temp;
- }
- // Check each direction
- for (var i = 0; i < directions.length; i++) {
- var dir = directions[i];
- var newRow = row + dir.row;
- var newCol = col + dir.col;
- // Check if the new cell is valid
- if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && !grid[newRow][newCol].visited) {
- // Remove walls between cells
- grid[row][col].walls[dir.wall] = false;
- grid[newRow][newCol].walls[dir.opposite] = false;
- // Continue with the new cell
- generateMaze(newRow, newCol);
+ // 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;
}
- // Start generating maze from a random cell
- generateMaze(Math.floor(Math.random() * rows), Math.floor(Math.random() * cols));
- // For more complex paths, remove some additional walls
- if (complexPaths) {
- var wallsToRemove = Math.floor(rows * cols * 0.15); // Remove about 15% of remaining walls
- for (var i = 0; i < wallsToRemove; i++) {
- var row = Math.floor(Math.random() * (rows - 1));
- var col = Math.floor(Math.random() * (cols - 1));
- // Randomly choose between removing a right wall or a bottom wall
- if (Math.random() < 0.5) {
- grid[row][col].walls.right = false;
- grid[row][col + 1].walls.left = false;
- } else {
- grid[row][col].walls.bottom = false;
- grid[row + 1][col].walls.top = false;
- }
- }
- }
- // Create the walls based on the grid
- for (var row = 0; row < rows; row++) {
- for (var col = 0; col < cols; col++) {
- var cell = grid[row][col];
- var x = wallThickness + col * cellWidth + cellWidth / 2;
- var y = wallThickness + row * cellHeight + cellHeight / 2;
- // Create walls for this cell
- if (cell.walls.top) {
- createWall(x, y - cellHeight / 2, cellWidth, wallThickness, 'inner');
- }
- if (cell.walls.right) {
- createWall(x + cellWidth / 2, y, wallThickness, cellHeight, 'inner');
- }
- if (row === rows - 1 && cell.walls.bottom) {
- createWall(x, y + cellHeight / 2, cellWidth, wallThickness, 'inner');
- }
- if (col === 0 && cell.walls.left) {
- createWall(x - cellWidth / 2, y, wallThickness, cellHeight, 'inner');
- }
- }
- }
}
-// Helper function to clear paths to important points
-function clearPathToPoint(targetX, targetY, radius) {
- for (var i = walls.length - 1; i >= 0; i--) {
- var wall = walls[i];
- var distance = Math.sqrt(Math.pow(wall.x - targetX, 2) + Math.pow(wall.y - targetY, 2));
- if (distance < radius && wall.wallType !== 'border') {
- walls.splice(i, 1);
- wall.destroy();
- }
- }
-}
function createUI() {
// Score text
scoreTxt = new Text2('Score: 0', {
size: 60,
@@ -954,13 +849,12 @@
instructionsContainer.visible = false;
game.addChild(instructionsContainer);
}
function showInstructions() {
- instructionsTxt.setText('Navigate the maze to rescue civilians and extinguish fires!\nDrag to move firefighter\nTap and hold to spray water\nRefill water at the fire station!');
instructionsContainer.visible = true;
LK.setTimeout(function () {
instructionsContainer.visible = false;
- }, 7000); // Show longer for maze instructions
+ }, 5000);
}
function startGameTimer() {
var timeRemaining = gameTime;
updateTimeBar(timeRemaining / gameTime);
@@ -1031,10 +925,10 @@
game.addChild(civilian);
civilians.push(civilian);
return civilian;
}
-function createWall(x, y, width, height, wallType) {
- var wall = new Wall(x, y, width, height, wallType);
+function createWall(x, y, width, height) {
+ var wall = new Wall(x, y, width, height);
game.addChild(wall);
walls.push(wall);
return wall;
}
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