User prompt
Now finish the game until it’s done
User prompt
Just add things that you think are good for the game until it’s done
User prompt
And that’s it
User prompt
and make the school dark but as light as the player can see the hallways
Code edit (1 edits merged)
Please save this source code
User prompt
Escape from Flumbo Jumbo
Initial prompt
Make a horror game where there is a monster named flumbo jumbo and you are in a school a abandoned one you have to collect 7 notebooks scattered around the school to escape when you escape there will be a cutscene of you going to the soft place center and that will be the end of the chapter’ and flumbo jumbo is blind’ if you make ANY sudden noise he will growl bang his chest and run to the noise when he growls there will be an adjective saying RUN AND GO FIND A LOCKER!! You have to go to the locker and hide in it and at the end flumbo jumbo will growl and suddenly his vision is better and he can see and when that happens the adjective will say HE IS NOT BLIND!!’ And you have to run to 6 exits of the school every exit will close except for the last one which is the 6th one and you slide through and then another cutscene comes and it shows him running and getting crushed by the exit door.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var DarknessMask = Container.expand(function () { var self = Container.call(this); // Create a dark overlay that covers the entire screen var darkness = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, alpha: 0.85, tint: 0x000000 }); // Scale to cover the entire screen darkness.width = 2048; darkness.height = 2732; self.addChild(darkness); // Create a visible circle around player (light effect) self.lightRadius = 250; // Reduced visibility radius for more tension self.adjustLightRadius = function (playerNoiseLevel) { // Light shrinks when making noise (harder to see when noisy) var targetRadius = 250 - playerNoiseLevel * 80; // Smooth transition with tween tween(self, { lightRadius: targetRadius }, { duration: 500 }); }; self.updatePosition = function (playerX, playerY) { self.x = 2048 / 2; self.y = 2732 / 2; // Create a mask using a circle asset at player's position if (!self.mask) { // Create a circle mask using a shape asset instead of Graphics self.mask = self.addChild(LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: self.lightRadius * 2, height: self.lightRadius * 2, tint: 0xFFFFFF })); } // Update mask position self.mask.x = playerX - self.x; self.mask.y = playerY - self.y; // Ensure mask size matches the desired light radius self.mask.width = self.lightRadius * 2; self.mask.height = self.lightRadius * 2; }; return self; }); var Exit = Container.expand(function () { var self = Container.call(this); var exitSprite = self.attachAsset('exit', { anchorX: 0.5, anchorY: 0.5 }); // Initially exits are inactive exitSprite.alpha = 0.3; self.activate = function () { exitSprite.alpha = 1; // Make the exit slightly pulse to draw attention function pulse() { tween(exitSprite, { scaleX: 1.2, scaleY: 1.2 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { tween(exitSprite, { scaleX: 1, scaleY: 1 }, { duration: 700, easing: tween.easeInOut, onFinish: pulse }); } }); } pulse(); }; return self; }); var Locker = Container.expand(function () { var self = Container.call(this); var lockerSprite = self.attachAsset('locker', { anchorX: 0.5, anchorY: 0.5 }); self.isOccupied = false; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); var monsterSprite = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.detectionRadius = 400; self.hasSight = false; self.targetX = 0; self.targetY = 0; self.isChasing = false; self.wanderTimer = 0; self.wanderDirection = { x: 0, y: 0 }; self.setHasSight = function (value) { self.hasSight = value; if (value) { tween(monsterSprite, { tint: 0xFF00FF }, { duration: 1000 }); self.speed = 3; // Show "HE IS NOT BLIND!!" message when monster gains sight if (instructionText) { instructionText.setText('HE IS NOT BLIND!!'); instructionText.style.fill = "#FF0000"; // Flash the screen to indicate danger LK.effects.flashScreen(0xFF0000, 500); } } }; self.startChasing = function (x, y) { self.isChasing = true; self.targetX = x; self.targetY = y; LK.getSound('monster_detect').play(); // Increase speed temporarily during initial chase self.speed = self.speed * 1.5; LK.setTimeout(function () { if (self.isChasing) { self.speed = self.speed / 1.5; } }, 3000); // Monster growls and bangs chest function bangChest() { tween(monsterSprite, { scaleX: 1.3, scaleY: 1.3, tint: 0xFF3300 }, { duration: 200, onFinish: function onFinish() { tween(monsterSprite, { scaleX: 1.0, scaleY: 1.0, tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { if (self.isChasing && LK.ticks % 2 === 0) { bangChest(); } } }); } }); } bangChest(); }; self.stopChasing = function () { self.isChasing = false; }; self.wander = function () { if (LK.ticks % 120 === 0 || self.wanderTimer <= 0) { // Pick a random direction var angle = Math.random() * Math.PI * 2; self.wanderDirection = { x: Math.cos(angle), y: Math.sin(angle) }; self.wanderTimer = 60 + Math.random() * 120; // Sometimes patrol near a locker if (Math.random() < 0.3 && lockers && lockers.length > 0) { // Target a random locker var randomLocker = lockers[Math.floor(Math.random() * lockers.length)]; self.targetX = randomLocker.x; self.targetY = randomLocker.y; // Move toward locker for a while LK.setTimeout(function () { // Reset after approaching self.targetX = 0; self.targetY = 0; }, 3000); } } // If targeting a specific location during wandering if (self.targetX !== 0 && self.targetY !== 0) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 150) { // Move toward target at reduced speed self.x += dx / dist * (self.speed * 0.3); self.y += dy / dist * (self.speed * 0.3); } else { // Wander normally when close to target self.x += self.wanderDirection.x * (self.speed * 0.5); self.y += self.wanderDirection.y * (self.speed * 0.5); } } else { // Normal wandering self.x += self.wanderDirection.x * (self.speed * 0.5); self.y += self.wanderDirection.y * (self.speed * 0.5); } // Boundary checks if (self.x < 100) self.x = 100; if (self.x > 2048 - 100) self.x = 2048 - 100; if (self.y < 100) self.y = 100; if (self.y > 2732 - 100) self.y = 2732 - 100; self.wanderTimer--; }; self.moveTowardsTarget = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Basic pathfinding - try to avoid obstacles var obstacleAhead = false; // Check if there's a direct obstacle in movement path for (var i = 0; walls && i < walls.length; i++) { // Check only walls directly in path var wallDx = walls[i].x - self.x; var wallDy = walls[i].y - self.y; var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy); // Check if wall is within immediate path and roughly in the same direction if (wallDist < 100 && (wallDx * dx + wallDy * dy) / (wallDist * distance) > 0.7) { obstacleAhead = true; break; } } if (obstacleAhead && Math.random() < 0.5) { // Add slight random variation to movement to navigate around obstacles var angle = Math.atan2(dy, dx) + (Math.random() * 0.8 - 0.4); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } else { // Direct movement toward target self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Occasionally increase speed in bursts when chasing if (LK.ticks % 180 === 0 && Math.random() < 0.3) { var burstSpeed = self.speed * 1.5; var originalSpeed = self.speed; self.speed = burstSpeed; LK.setTimeout(function () { self.speed = originalSpeed; }, 500); } } } }; self.update = function () { if (self.isChasing) { self.moveTowardsTarget(); } else { self.wander(); } }; return self; }); var Notebook = Container.expand(function () { var self = Container.call(this); var notebookSprite = self.attachAsset('notebook', { anchorX: 0.5, anchorY: 0.5 }); // Make the notebook slightly pulse to draw attention function pulse() { tween(notebookSprite, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(notebookSprite, { scaleX: 1, scaleY: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: pulse }); } }); } pulse(); return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.isHidden = false; self.noiseLevel = 0; self.notebooksCollected = 0; self.makeNoise = function (amount) { self.noiseLevel = Math.min(1, self.noiseLevel + amount); // Noise decreases over time LK.setTimeout(function () { self.noiseLevel = Math.max(0, self.noiseLevel - 0.1); }, 1000); }; self.hide = function () { if (!self.isHidden) { self.isHidden = true; // Fade out effect tween(playerSprite, { alpha: 0.3, scaleX: 0.8, scaleY: 0.8 }, { duration: 300 }); LK.getSound('hide').play(); // Heavy breathing effect when monster is nearby if (monster && monster.isChasing) { // Trembling effect while hiding from monster var _tremble = function tremble() { if (self.isHidden && monster.isChasing) { var trembleAmt = 0.05; playerSprite.x = Math.random() * trembleAmt * 2 - trembleAmt; playerSprite.y = Math.random() * trembleAmt * 2 - trembleAmt; LK.setTimeout(_tremble, 100); } else { playerSprite.x = 0; playerSprite.y = 0; } }; _tremble(); } } }; self.unhide = function () { if (self.isHidden) { self.isHidden = false; playerSprite.alpha = 1; } }; self.update = function () { // Player visibility logic is handled in the main game update if (self.isMoving && !self.isHidden) { if (LK.ticks % 20 === 0) { LK.getSound('footstep').play(); self.makeNoise(0.15); // Add subtle visual feedback for footsteps tween(playerSprite, { scaleX: 0.95, scaleY: 1.05, y: 5 }, { duration: 150, onFinish: function onFinish() { tween(playerSprite, { scaleX: 1, scaleY: 1, y: 0 }, { duration: 150 }); } }); } // Create footstep effect if (LK.ticks % 30 === 0 && self.noiseLevel > 0.4) { // Add visual ripple for footsteps when being noisy var footstepEffect = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, alpha: 0.3, width: 20, height: 20, tint: 0xFFFFFF }); self.addChild(footstepEffect); // Animate and remove tween(footstepEffect, { width: 60, height: 60, alpha: 0 }, { duration: 500, onFinish: function onFinish() { self.removeChild(footstepEffect); } }); } } }; return self; }); var Wall = Container.expand(function () { var self = Container.call(this); self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x050505 }); /**** * Game Code ****/ // Game variables var player; var monster; var notebooks = []; var lockers = []; var exits = []; var walls = []; var notebooksRequired = 7; var lastTouchedLocker = null; var gamePhase = "collecting"; // collecting, escaping // UI elements var notebookCounter; var noiseIndicator; var instructionText; // Create the UI function setupUI() { // Notebook counter notebookCounter = new Text2('0/' + notebooksRequired, { size: 80, fill: 0xFFD700 }); notebookCounter.anchor.set(1, 0); LK.gui.topRight.addChild(notebookCounter); // Noise indicator noiseIndicator = new Text2('Noise: ●', { size: 60, fill: 0x00FF00 }); noiseIndicator.anchor.set(0, 0); LK.gui.topRight.addChild(noiseIndicator); noiseIndicator.y = 100; // Instruction text instructionText = new Text2('Find and collect 7 notebooks to escape!', { size: 50, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0); LK.gui.top.addChild(instructionText); instructionText.y = 20; } // Generate a random position that isn't too close to existing objects function getRandomPosition(minDistance) { var x, y; var validPosition = false; while (!validPosition) { x = 150 + Math.random() * (2048 - 300); y = 150 + Math.random() * (2732 - 300); validPosition = true; // Check distance from player if (player) { var dxPlayer = x - player.x; var dyPlayer = y - player.y; var distPlayer = Math.sqrt(dxPlayer * dxPlayer + dyPlayer * dyPlayer); if (distPlayer < minDistance) { validPosition = false; continue; } } // Check distance from other notebooks for (var i = 0; i < notebooks.length; i++) { var dxNote = x - notebooks[i].x; var dyNote = y - notebooks[i].y; var distNote = Math.sqrt(dxNote * dxNote + dyNote * dyNote); if (distNote < minDistance) { validPosition = false; break; } } // Check distance from lockers for (var j = 0; j < lockers.length; j++) { var dxLocker = x - lockers[j].x; var dyLocker = y - lockers[j].y; var distLocker = Math.sqrt(dxLocker * dxLocker + dyLocker * dyLocker); if (distLocker < minDistance) { validPosition = false; break; } } } return { x: x, y: y }; } // Generate the game level function setupLevel() { // Create player player = new Player(); player.x = 2048 / 2; player.y = 2732 / 2; game.addChild(player); // Add darkness overlay with light around player var darknessMask = new DarknessMask(); darknessMask.updatePosition(player.x, player.y); game.addChild(darknessMask); // Create school environment with walls function createSchoolWalls() { // Create outer walls (school boundaries) // Top wall for (var x = 0; x < 2048; x += 50) { var topWall = new Wall(); topWall.x = x; topWall.y = 25; walls.push(topWall); game.addChild(topWall); } // Bottom wall for (var x = 0; x < 2048; x += 50) { var bottomWall = new Wall(); bottomWall.x = x; bottomWall.y = 2732 - 25; walls.push(bottomWall); game.addChild(bottomWall); } // Left wall for (var y = 0; y < 2732; y += 50) { var leftWall = new Wall(); leftWall.x = 25; leftWall.y = y; walls.push(leftWall); game.addChild(leftWall); } // Right wall for (var y = 0; y < 2732; y += 50) { var rightWall = new Wall(); rightWall.x = 2048 - 25; rightWall.y = y; walls.push(rightWall); game.addChild(rightWall); } // Create internal school hallways and rooms // Create random corridors var corridorCount = 8; for (var i = 0; i < corridorCount; i++) { var isHorizontal = Math.random() > 0.5; var startX = 100 + Math.random() * (2048 - 200); var startY = 100 + Math.random() * (2732 - 200); var length = 300 + Math.random() * 500; if (isHorizontal) { for (var x = 0; x < length; x += 100) { if (startX + x < 2048 - 100) { // Add walls on sides of corridor var topWall = new Wall(); topWall.x = startX + x; topWall.y = startY - 50; walls.push(topWall); game.addChild(topWall); var bottomWall = new Wall(); bottomWall.x = startX + x; bottomWall.y = startY + 50; walls.push(bottomWall); game.addChild(bottomWall); } } } else { for (var y = 0; y < length; y += 100) { if (startY + y < 2732 - 100) { // Add walls on sides of corridor var leftWall = new Wall(); leftWall.x = startX - 50; leftWall.y = startY + y; walls.push(leftWall); game.addChild(leftWall); var rightWall = new Wall(); rightWall.x = startX + 50; rightWall.y = startY + y; walls.push(rightWall); game.addChild(rightWall); } } } } } createSchoolWalls(); // Create monster monster = new Monster(); monster.x = 300; monster.y = 300; game.addChild(monster); // Create notebooks for (var i = 0; i < notebooksRequired; i++) { var pos = getRandomPosition(300); var notebook = new Notebook(); notebook.x = pos.x; notebook.y = pos.y; notebooks.push(notebook); game.addChild(notebook); } // Create lockers var lockerCount = 12; for (var j = 0; j < lockerCount; j++) { var lockerPos = getRandomPosition(250); var locker = new Locker(); locker.x = lockerPos.x; locker.y = lockerPos.y; lockers.push(locker); game.addChild(locker); } // Create exits (initially inactive) var exitPositions = [{ x: 100, y: 100 }, { x: 2048 - 100, y: 100 }, { x: 2048 / 2, y: 100 }, { x: 100, y: 2732 - 100 }, { x: 2048 - 100, y: 2732 - 100 }, { x: 2048 / 2, y: 2732 - 100 }]; for (var k = 0; k < exitPositions.length; k++) { var exit = new Exit(); exit.x = exitPositions[k].x; exit.y = exitPositions[k].y; exits.push(exit); game.addChild(exit); } } // Start the game function startGame() { setupUI(); setupLevel(); // Play ambient music LK.playMusic('ambient'); // Show tutorial message var tutorialText = new Text2('Drag to move. Find notebooks.\nFlumbo Jumbo is blind but can hear you.\nMove carefully to stay quiet.\nHide in lockers when detected!', { size: 40, fill: 0xFFFFFF }); tutorialText.anchor.set(0.5, 0.5); tutorialText.x = 2048 / 2; tutorialText.y = 2732 / 2; game.addChild(tutorialText); // Fade out tutorial after a delay LK.setTimeout(function () { tween(tutorialText, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { game.removeChild(tutorialText); } }); }, 5000); } // Input handling var isDragging = false; var lastTouchX = 0; var lastTouchY = 0; game.down = function (x, y, obj) { isDragging = true; lastTouchX = x; lastTouchY = y; }; game.up = function (x, y, obj) { isDragging = false; player.isMoving = false; }; game.move = function (x, y, obj) { if (isDragging && !player.isHidden) { // Calculate direction and distance var dx = x - lastTouchX; var dy = y - lastTouchY; var distance = Math.sqrt(dx * dx + dy * dy); // Only move if there's significant movement if (distance > 5) { player.isMoving = true; // Calculate speed based on noise level - more careful = less noise var speedMultiplier = distance > 30 ? 0.6 : 0.4; player.x += dx * speedMultiplier; player.y += dy * speedMultiplier; // If moving fast, generate more noise if (distance > 30) { player.makeNoise(0.01); } // Keep player in bounds if (player.x < 50) player.x = 50; if (player.x > 2048 - 50) player.x = 2048 - 50; if (player.y < 50) player.y = 50; if (player.y > 2732 - 50) player.y = 2732 - 50; // Update last position lastTouchX = x; lastTouchY = y; } else { player.isMoving = false; } } }; // Update game state game.update = function () { if (!player) { startGame(); return; } // Update noise indicator if (player.noiseLevel < 0.3) { noiseIndicator.setText('Noise: ●'); noiseIndicator.style.fill = "#00FF00"; } else if (player.noiseLevel < 0.7) { noiseIndicator.setText('Noise: ●●'); noiseIndicator.style.fill = "#FFFF00"; } else { noiseIndicator.setText('Noise: ●●●'); noiseIndicator.style.fill = "#FF0000"; } // Check for notebook collection for (var i = notebooks.length - 1; i >= 0; i--) { var notebook = notebooks[i]; if (player.intersects(notebook) && !player.isHidden) { // Remove the notebook notebook.destroy(); notebooks.splice(i, 1); // Update player stats player.notebooksCollected++; notebookCounter.setText(player.notebooksCollected + '/' + notebooksRequired); // Play sound LK.getSound('pickup').play(); // Make some noise when collecting player.makeNoise(0.3); // Visual effect for notebook collection var collectEffect = LK.getAsset('notebook', { anchorX: 0.5, anchorY: 0.5, x: notebook.x, y: notebook.y, alpha: 0.8, scaleX: 1, scaleY: 1 }); game.addChild(collectEffect); // Animation for notebook collection tween(collectEffect, { scaleX: 2, scaleY: 2, alpha: 0, y: collectEffect.y - 100 }, { duration: 800, onFinish: function onFinish() { game.removeChild(collectEffect); } }); // Check if all notebooks are collected if (player.notebooksCollected >= notebooksRequired) { gamePhase = "escaping"; instructionText.setText('All notebooks collected! Find an exit before Flumbo catches you!'); monster.setHasSight(true); // Dramatic effect when monster gains sight LK.effects.flashScreen(0xFF0000, 800); // Only activate the last exit (6th one) as instructed // The other exits should be visible but closed for (var e = 0; e < exits.length; e++) { if (e === exits.length - 1) { // Activate only the last exit exits[e].activate(); // Make a subtle pulsing effect to hint at correct exit LK.setTimeout(function () { tween(exits[exits.length - 1].children[0], { alpha: 0.7 }, { duration: 500, onFinish: function onFinish() { tween(exits[exits.length - 1].children[0], { alpha: 1 }, { duration: 500, onFinish: function onFinish() { if (gamePhase === "escaping") { this.onFinish(); } } }); } }); }, 1000); } else { // Make other exits visible but not usable var exitSprite = exits[e].children[0]; exitSprite.alpha = 0.5; exitSprite.tint = 0x880000; // Red tint to indicate closed } } } } } // Check for hiding in lockers var nearLocker = false; for (var j = 0; j < lockers.length; j++) { var locker = lockers[j]; var dxLocker = player.x - locker.x; var dyLocker = player.y - locker.y; var distLocker = Math.sqrt(dxLocker * dxLocker + dyLocker * dyLocker); if (distLocker < 100 && !locker.isOccupied) { nearLocker = true; lastTouchedLocker = locker; // Double tap or tap when monster is chasing to hide if (isDragging && (monster.isChasing || LK.ticks % 30 === 0)) { player.hide(); player.x = locker.x; player.y = locker.y; locker.isOccupied = true; break; } } } // Allow player to exit locker when not being chased if (player.isHidden && !monster.isChasing && isDragging) { player.unhide(); if (lastTouchedLocker) { lastTouchedLocker.isOccupied = false; } } // Check for reaching an exit in escape phase if (gamePhase === "escaping") { for (var k = 0; k < exits.length; k++) { var exit = exits[k]; if (player.intersects(exit) && !player.isHidden) { // Check if this is the last exit (the only open one) if (k === exits.length - 1) { // Play escape cutscene player.isHidden = true; // Prevent further interaction monster.isChasing = true; monster.targetX = player.x; monster.targetY = player.y; // Player slides through the exit tween(player, { x: exit.x, y: exit.y, alpha: 0.5, scaleX: 0.5, scaleY: 0.5 }, { duration: 1000, onFinish: function onFinish() { // Monster gets crushed by the door - dramatic chase sequence tween(monster, { x: exit.x - 50, y: exit.y }, { duration: 600, onFinish: function onFinish() { // Monster tries to reach player in last desperate attempt tween(monster.children[0], { scaleX: 1.3, scaleY: 1.1, tint: 0xFF0000 }, { duration: 300, onFinish: function onFinish() { // Door closes on monster LK.effects.flashScreen(0xFF0000, 500); // Play monster detection sound for impact LK.getSound('monster_detect').play(); // Flatten monster to show crushing tween(monster.children[0], { scaleY: 0.2, scaleX: 1.8, alpha: 0.5, tint: 0x660000 }, { duration: 700, onFinish: function onFinish() { // Show "soft place center" message instructionText.setText("You've escaped to the soft place center! Chapter complete."); instructionText.style.fill = "#00FF00"; // Create final escape visual effect var light = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: exit.x, y: exit.y, alpha: 0, width: 50, height: 50, tint: 0xFFFFFF }); game.addChild(light); tween(light, { alpha: 0.8, width: 2048, height: 2048 }, { duration: 1500, onFinish: function onFinish() { LK.setTimeout(function () { LK.showYouWin(); }, 500); } }); } }); } }); } }); } }); return; } else { // Closed exit - monster gets angrier LK.effects.flashScreen(0xFF0000, 300); monster.speed += 0.5; monster.startChasing(player.x, player.y); } } } } // Monster detection var dxMonster = monster.x - player.x; var dyMonster = monster.y - player.y; var distMonster = Math.sqrt(dxMonster * dxMonster + dyMonster * dyMonster); // Proximity warning - monster is getting close if (!player.isHidden && distMonster < monster.detectionRadius * 1.5 && !monster.isChasing) { // Visual proximity indicator if (LK.ticks % 60 === 0) { LK.effects.flashScreen(0x330000, 300); // Increase player fear based on proximity var proximityFear = 1 - distMonster / (monster.detectionRadius * 1.5); player.makeNoise(proximityFear * 0.1); // Fear increases noise level slightly // Heartbeat effect - the closer the monster, the faster the heartbeat var heartbeatIntensity = 1 - distMonster / (monster.detectionRadius * 1.5); // Pulse the player slightly to simulate heartbeat tween(player, { scaleX: 1 + heartbeatIntensity * 0.1, scaleY: 1 + heartbeatIntensity * 0.1 }, { duration: 300 - heartbeatIntensity * 200, onFinish: function onFinish() { tween(player, { scaleX: 1, scaleY: 1 }, { duration: 300 - heartbeatIntensity * 200 }); } }); } } // Monster detects player based on noise if (!player.isHidden && distMonster < monster.detectionRadius * player.noiseLevel) { monster.startChasing(player.x, player.y); // Show warning message as instructed instructionText.setText('RUN AND GO FIND A LOCKER!!'); instructionText.style.fill = "#FF0000"; } // Monster can see player in the final phase else if (gamePhase === "escaping" && monster.hasSight && !player.isHidden && distMonster < monster.detectionRadius * 1.5) { monster.startChasing(player.x, player.y); } // Update monster's target position if already chasing else if (monster.isChasing && !player.isHidden) { monster.targetX = player.x; monster.targetY = player.y; } // Stop chasing if player is hidden else if (player.isHidden && monster.isChasing) { LK.setTimeout(function () { monster.stopChasing(); }, 1500); } // Stop chasing if lost track for too long else if (monster.isChasing && LK.ticks % 180 === 0) { monster.stopChasing(); } // Check for collisions with walls function checkWallCollisions() { // Check player-wall collisions for (var i = 0; i < walls.length; i++) { var wall = walls[i]; if (player.intersects(wall) && !player.isHidden) { // Calculate push back direction var dx = player.x - wall.x; var dy = player.y - wall.y; var dist = Math.sqrt(dx * dx + dy * dy); // Push player away from wall if (dist > 0) { // Calculate minimal push needed var overlapX = (player.width + wall.width) / 2 - Math.abs(dx); var overlapY = (player.height + wall.height) / 2 - Math.abs(dy); // Push in the direction of minimum overlap if (overlapX < overlapY) { player.x += dx / dist * overlapX * 1.1; } else { player.y += dy / dist * overlapY * 1.1; } } } } // Check monster-wall collisions (less strict, monster can navigate around walls better) if (monster.isChasing) { for (var i = 0; i < walls.length; i++) { var wall = walls[i]; if (monster.intersects(wall)) { // Less strict collision for monster - allows some navigation through gaps var dx = monster.x - wall.x; var dy = monster.y - wall.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { monster.x += dx / dist * 2; monster.y += dy / dist * 2; } } } } } checkWallCollisions(); // Update darkness mask to follow player for (var i = 0; i < game.children.length; i++) { if (game.children[i] instanceof DarknessMask) { game.children[i].updatePosition(player.x, player.y); // Also adjust light radius based on player noise game.children[i].adjustLightRadius(player.noiseLevel); break; } } // Check for player caught by monster if (!player.isHidden && player.intersects(monster)) { // Flash the screen red LK.effects.flashScreen(0xFF0000, 500); // Game over LK.showGameOver(); } };
===================================================================
--- original.js
+++ change.js
@@ -45,10 +45,9 @@
anchorX: 0.5,
anchorY: 0.5,
width: self.lightRadius * 2,
height: self.lightRadius * 2,
- tint: 0xFFFFFF,
- shape: 'ellipse'
+ tint: 0xFFFFFF
}));
}
// Update mask position
self.mask.x = playerX - self.x;
@@ -231,10 +230,41 @@
var dx = self.targetX - self.x;
var dy = self.targetY - 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;
+ // Basic pathfinding - try to avoid obstacles
+ var obstacleAhead = false;
+ // Check if there's a direct obstacle in movement path
+ for (var i = 0; walls && i < walls.length; i++) {
+ // Check only walls directly in path
+ var wallDx = walls[i].x - self.x;
+ var wallDy = walls[i].y - self.y;
+ var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
+ // Check if wall is within immediate path and roughly in the same direction
+ if (wallDist < 100 && (wallDx * dx + wallDy * dy) / (wallDist * distance) > 0.7) {
+ obstacleAhead = true;
+ break;
+ }
+ }
+ if (obstacleAhead && Math.random() < 0.5) {
+ // Add slight random variation to movement to navigate around obstacles
+ var angle = Math.atan2(dy, dx) + (Math.random() * 0.8 - 0.4);
+ self.x += Math.cos(angle) * self.speed;
+ self.y += Math.sin(angle) * self.speed;
+ } else {
+ // Direct movement toward target
+ self.x += dx / distance * self.speed;
+ self.y += dy / distance * self.speed;
+ // Occasionally increase speed in bursts when chasing
+ if (LK.ticks % 180 === 0 && Math.random() < 0.3) {
+ var burstSpeed = self.speed * 1.5;
+ var originalSpeed = self.speed;
+ self.speed = burstSpeed;
+ LK.setTimeout(function () {
+ self.speed = originalSpeed;
+ }, 500);
+ }
+ }
}
};
self.update = function () {
if (self.isChasing) {
@@ -350,8 +380,34 @@
});
}
});
}
+ // Create footstep effect
+ if (LK.ticks % 30 === 0 && self.noiseLevel > 0.4) {
+ // Add visual ripple for footsteps when being noisy
+ var footstepEffect = LK.getAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 0,
+ y: 0,
+ alpha: 0.3,
+ width: 20,
+ height: 20,
+ tint: 0xFFFFFF
+ });
+ self.addChild(footstepEffect);
+ // Animate and remove
+ tween(footstepEffect, {
+ width: 60,
+ height: 60,
+ alpha: 0
+ }, {
+ duration: 500,
+ onFinish: function onFinish() {
+ self.removeChild(footstepEffect);
+ }
+ });
+ }
}
};
return self;
});
@@ -468,8 +524,87 @@
// Add darkness overlay with light around player
var darknessMask = new DarknessMask();
darknessMask.updatePosition(player.x, player.y);
game.addChild(darknessMask);
+ // Create school environment with walls
+ function createSchoolWalls() {
+ // Create outer walls (school boundaries)
+ // Top wall
+ for (var x = 0; x < 2048; x += 50) {
+ var topWall = new Wall();
+ topWall.x = x;
+ topWall.y = 25;
+ walls.push(topWall);
+ game.addChild(topWall);
+ }
+ // Bottom wall
+ for (var x = 0; x < 2048; x += 50) {
+ var bottomWall = new Wall();
+ bottomWall.x = x;
+ bottomWall.y = 2732 - 25;
+ walls.push(bottomWall);
+ game.addChild(bottomWall);
+ }
+ // Left wall
+ for (var y = 0; y < 2732; y += 50) {
+ var leftWall = new Wall();
+ leftWall.x = 25;
+ leftWall.y = y;
+ walls.push(leftWall);
+ game.addChild(leftWall);
+ }
+ // Right wall
+ for (var y = 0; y < 2732; y += 50) {
+ var rightWall = new Wall();
+ rightWall.x = 2048 - 25;
+ rightWall.y = y;
+ walls.push(rightWall);
+ game.addChild(rightWall);
+ }
+ // Create internal school hallways and rooms
+ // Create random corridors
+ var corridorCount = 8;
+ for (var i = 0; i < corridorCount; i++) {
+ var isHorizontal = Math.random() > 0.5;
+ var startX = 100 + Math.random() * (2048 - 200);
+ var startY = 100 + Math.random() * (2732 - 200);
+ var length = 300 + Math.random() * 500;
+ if (isHorizontal) {
+ for (var x = 0; x < length; x += 100) {
+ if (startX + x < 2048 - 100) {
+ // Add walls on sides of corridor
+ var topWall = new Wall();
+ topWall.x = startX + x;
+ topWall.y = startY - 50;
+ walls.push(topWall);
+ game.addChild(topWall);
+ var bottomWall = new Wall();
+ bottomWall.x = startX + x;
+ bottomWall.y = startY + 50;
+ walls.push(bottomWall);
+ game.addChild(bottomWall);
+ }
+ }
+ } else {
+ for (var y = 0; y < length; y += 100) {
+ if (startY + y < 2732 - 100) {
+ // Add walls on sides of corridor
+ var leftWall = new Wall();
+ leftWall.x = startX - 50;
+ leftWall.y = startY + y;
+ walls.push(leftWall);
+ game.addChild(leftWall);
+ var rightWall = new Wall();
+ rightWall.x = startX + 50;
+ rightWall.y = startY + y;
+ walls.push(rightWall);
+ game.addChild(rightWall);
+ }
+ }
+ }
+ }
+ }
+ createSchoolWalls();
// Create monster
monster = new Monster();
monster.x = 300;
monster.y = 300;
@@ -526,8 +661,28 @@
setupUI();
setupLevel();
// Play ambient music
LK.playMusic('ambient');
+ // Show tutorial message
+ var tutorialText = new Text2('Drag to move. Find notebooks.\nFlumbo Jumbo is blind but can hear you.\nMove carefully to stay quiet.\nHide in lockers when detected!', {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ tutorialText.anchor.set(0.5, 0.5);
+ tutorialText.x = 2048 / 2;
+ tutorialText.y = 2732 / 2;
+ game.addChild(tutorialText);
+ // Fade out tutorial after a delay
+ LK.setTimeout(function () {
+ tween(tutorialText, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ onFinish: function onFinish() {
+ game.removeChild(tutorialText);
+ }
+ });
+ }, 5000);
}
// Input handling
var isDragging = false;
var lastTouchX = 0;
@@ -601,8 +756,31 @@
// Play sound
LK.getSound('pickup').play();
// Make some noise when collecting
player.makeNoise(0.3);
+ // Visual effect for notebook collection
+ var collectEffect = LK.getAsset('notebook', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: notebook.x,
+ y: notebook.y,
+ alpha: 0.8,
+ scaleX: 1,
+ scaleY: 1
+ });
+ game.addChild(collectEffect);
+ // Animation for notebook collection
+ tween(collectEffect, {
+ scaleX: 2,
+ scaleY: 2,
+ alpha: 0,
+ y: collectEffect.y - 100
+ }, {
+ duration: 800,
+ onFinish: function onFinish() {
+ game.removeChild(collectEffect);
+ }
+ });
// Check if all notebooks are collected
if (player.notebooksCollected >= notebooksRequired) {
gamePhase = "escaping";
instructionText.setText('All notebooks collected! Find an exit before Flumbo catches you!');
@@ -693,30 +871,65 @@
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
- // Monster gets crushed by the door
+ // Monster gets crushed by the door - dramatic chase sequence
tween(monster, {
- x: exit.x,
+ x: exit.x - 50,
y: exit.y
}, {
- duration: 800,
+ duration: 600,
onFinish: function onFinish() {
- LK.effects.flashScreen(0xFF0000, 300);
- // Flatten monster to show crushing
+ // Monster tries to reach player in last desperate attempt
tween(monster.children[0], {
- scaleY: 0.2,
- scaleX: 1.5,
- alpha: 0.5
+ scaleX: 1.3,
+ scaleY: 1.1,
+ tint: 0xFF0000
}, {
- duration: 500,
+ duration: 300,
onFinish: function onFinish() {
- // Show "soft place center" message
- instructionText.setText("You've escaped to the soft place center! Chapter complete.");
- instructionText.style.fill = "#00FF00";
- LK.setTimeout(function () {
- LK.showYouWin();
- }, 2000);
+ // Door closes on monster
+ LK.effects.flashScreen(0xFF0000, 500);
+ // Play monster detection sound for impact
+ LK.getSound('monster_detect').play();
+ // Flatten monster to show crushing
+ tween(monster.children[0], {
+ scaleY: 0.2,
+ scaleX: 1.8,
+ alpha: 0.5,
+ tint: 0x660000
+ }, {
+ duration: 700,
+ onFinish: function onFinish() {
+ // Show "soft place center" message
+ instructionText.setText("You've escaped to the soft place center! Chapter complete.");
+ instructionText.style.fill = "#00FF00";
+ // Create final escape visual effect
+ var light = LK.getAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: exit.x,
+ y: exit.y,
+ alpha: 0,
+ width: 50,
+ height: 50,
+ tint: 0xFFFFFF
+ });
+ game.addChild(light);
+ tween(light, {
+ alpha: 0.8,
+ width: 2048,
+ height: 2048
+ }, {
+ duration: 1500,
+ onFinish: function onFinish() {
+ LK.setTimeout(function () {
+ LK.showYouWin();
+ }, 500);
+ }
+ });
+ }
+ });
}
});
}
});
@@ -743,8 +956,25 @@
LK.effects.flashScreen(0x330000, 300);
// Increase player fear based on proximity
var proximityFear = 1 - distMonster / (monster.detectionRadius * 1.5);
player.makeNoise(proximityFear * 0.1); // Fear increases noise level slightly
+ // Heartbeat effect - the closer the monster, the faster the heartbeat
+ var heartbeatIntensity = 1 - distMonster / (monster.detectionRadius * 1.5);
+ // Pulse the player slightly to simulate heartbeat
+ tween(player, {
+ scaleX: 1 + heartbeatIntensity * 0.1,
+ scaleY: 1 + heartbeatIntensity * 0.1
+ }, {
+ duration: 300 - heartbeatIntensity * 200,
+ onFinish: function onFinish() {
+ tween(player, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 300 - heartbeatIntensity * 200
+ });
+ }
+ });
}
}
// Monster detects player based on noise
if (!player.isHidden && distMonster < monster.detectionRadius * player.noiseLevel) {
@@ -771,8 +1001,50 @@
// Stop chasing if lost track for too long
else if (monster.isChasing && LK.ticks % 180 === 0) {
monster.stopChasing();
}
+ // Check for collisions with walls
+ function checkWallCollisions() {
+ // Check player-wall collisions
+ for (var i = 0; i < walls.length; i++) {
+ var wall = walls[i];
+ if (player.intersects(wall) && !player.isHidden) {
+ // Calculate push back direction
+ var dx = player.x - wall.x;
+ var dy = player.y - wall.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ // Push player away from wall
+ if (dist > 0) {
+ // Calculate minimal push needed
+ var overlapX = (player.width + wall.width) / 2 - Math.abs(dx);
+ var overlapY = (player.height + wall.height) / 2 - Math.abs(dy);
+ // Push in the direction of minimum overlap
+ if (overlapX < overlapY) {
+ player.x += dx / dist * overlapX * 1.1;
+ } else {
+ player.y += dy / dist * overlapY * 1.1;
+ }
+ }
+ }
+ }
+ // Check monster-wall collisions (less strict, monster can navigate around walls better)
+ if (monster.isChasing) {
+ for (var i = 0; i < walls.length; i++) {
+ var wall = walls[i];
+ if (monster.intersects(wall)) {
+ // Less strict collision for monster - allows some navigation through gaps
+ var dx = monster.x - wall.x;
+ var dy = monster.y - wall.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 0) {
+ monster.x += dx / dist * 2;
+ monster.y += dy / dist * 2;
+ }
+ }
+ }
+ }
+ }
+ checkWallCollisions();
// Update darkness mask to follow player
for (var i = 0; i < game.children.length; i++) {
if (game.children[i] instanceof DarknessMask) {
game.children[i].updatePosition(player.x, player.y);
Make a mascot evil creature. In-Game asset. 2d. High contrast. No shadows
A dirty notebook like it was in a abandoned school. In-Game asset. 2d. High contrast. No shadows
Make a scary dark locker that is blue and make it like it was in a abandoned school. In-Game asset. 2d. High contrast. No shadows
Make a wall like it was in a abandoned school. In-Game asset. 2d. High contrast. No shadows
Make a door with a white wall like it was in an abandoned school on top of the door is a glowing red exit sign. In-Game asset. 2d. High contrast. No shadows
Make a centercircle in an abandoned school. In-Game asset. 2d. High contrast. No shadows