/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Baldi = Container.expand(function () { var self = Container.call(this); var baldiGraphics = self.attachAsset('baldiShape', { anchorX: 0.5, anchorY: 0.5 }); self.baseSpeed = 2; self.speed = self.baseSpeed; self.targetX = 0; self.targetY = 0; self.angerLevel = 0; self.lastRulerSound = 0; self.setTarget = function (x, y) { self.targetX = x; self.targetY = y; }; self.update = function () { // Move towards player var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Normalize direction dx = dx / distance; dy = dy / distance; // Apply movement with current speed self.x += dx * self.speed; self.y += dy * self.speed; // Play ruler slap sound periodically var currentTime = Date.now(); if (currentTime - self.lastRulerSound > 1500 / (1 + self.angerLevel * 0.2)) { LK.getSound('ruler').play(); self.lastRulerSound = currentTime; } } }; self.increaseAnger = function () { self.angerLevel++; self.speed = self.baseSpeed + self.angerLevel * 0.5; tween(baldiGraphics, { tint: 0xff0000 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { tween(baldiGraphics, { tint: 0xffffff }, { duration: 500, easing: tween.easeIn }); } }); }; return self; }); var Door = Container.expand(function () { var self = Container.call(this); var doorGraphics = self.attachAsset('doorShape', { anchorX: 0.5, anchorY: 0.5 }); self.isLocked = false; self.isExit = false; self.setAsExit = function () { self.isExit = true; self.isLocked = true; doorGraphics.destroy(); var exitDoorGraphics = self.attachAsset('exitDoorShape', { anchorX: 0.5, anchorY: 0.5 }); return self; }; return self; }); var Notebook = Container.expand(function () { var self = Container.call(this); var notebookGraphics = self.attachAsset('notebookShape', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.problems = []; self.generateProblems = function () { self.problems = []; for (var i = 0; i < 3; i++) { var difficulty = game.notebooksCollected; var problem; if (difficulty < 2) { // Simple addition problems var a = Math.floor(Math.random() * 10) + 1; var b = Math.floor(Math.random() * 10) + 1; problem = { question: a + " + " + b + " = ?", answer: a + b }; } else if (difficulty < 4) { // Medium difficulty problems (addition/subtraction) var a = Math.floor(Math.random() * 20) + 1; var b = Math.floor(Math.random() * 10) + 1; var isAddition = Math.random() > 0.5; if (isAddition) { problem = { question: a + " + " + b + " = ?", answer: a + b }; } else { // Ensure a > b for subtraction if (a < b) { var temp = a; a = b; b = temp; } problem = { question: a + " - " + b + " = ?", answer: a - b }; } } else { // Hard problems (multiplication, division) if (Math.random() > 0.5) { // Multiplication var a = Math.floor(Math.random() * 12) + 1; var b = Math.floor(Math.random() * 12) + 1; problem = { question: a + " × " + b + " = ?", answer: a * b }; } else { // Division (make sure it's clean division) var b = Math.floor(Math.random() * 10) + 1; var result = Math.floor(Math.random() * 10) + 1; var a = b * result; problem = { question: a + " ÷ " + b + " = ?", answer: result }; } } // Add occasional trick questions in later notebooks if (difficulty > 3 && Math.random() < 0.2) { problem = { question: Math.random() < 0.5 ? "What is Baldi's favorite number?" : "What sound does a bee make?", answer: "IMPOSSIBLE", isTrick: true }; } self.problems.push(problem); } return self.problems; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('playerShape', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 7; self.maxStamina = 100; self.stamina = self.maxStamina; self.isRunning = false; self.lastFootstepTime = 0; self.move = function (dx, dy) { var moveSpeed = self.speed; if (self.isRunning && self.stamina > 0) { moveSpeed *= 1.8; self.stamina -= 0.8; if (self.stamina < 0) { self.stamina = 0; } } else { // Recover stamina when not running if (!self.isRunning && self.stamina < self.maxStamina) { self.stamina += 0.3; if (self.stamina > self.maxStamina) { self.stamina = self.maxStamina; } } } // Normalize diagonal movement if (dx !== 0 && dy !== 0) { var length = Math.sqrt(dx * dx + dy * dy); dx = dx / length; dy = dy / length; } // Apply movement var newX = self.x + dx * moveSpeed; var newY = self.y + dy * moveSpeed; // Wall collision will be checked in the game.update method self.x = newX; self.y = newY; // Play footstep sound periodically while moving if (dx !== 0 || dy !== 0) { var currentTime = Date.now(); if (currentTime - self.lastFootstepTime > (self.isRunning ? 250 : 350)) { LK.getSound('footstep').play(); self.lastFootstepTime = currentTime; } } }; self.collidesWith = function (obj) { return self.intersects(obj); }; return self; }); var Principal = Container.expand(function () { var self = Container.call(this); var principalGraphics = self.attachAsset('principalShape', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.patrolPoints = []; self.currentPatrolIndex = 0; self.waitCounter = 0; self.waitTime = 60; // frames to wait at each patrol point self.isChasing = false; self.chasedPlayer = null; self.detentionDuration = 180; // 3 seconds at 60fps self.setPatrolPoints = function (points) { self.patrolPoints = points; if (points.length > 0) { self.x = points[0].x; self.y = points[0].y; } return self; }; self.update = function () { if (self.isChasing && self.chasedPlayer) { // Chase the running player var dx = self.chasedPlayer.x - self.x; var dy = self.chasedPlayer.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 40) { // Caught the player - put in detention self.isChasing = false; game.putPlayerInDetention(); return; } // Move towards player if (distance > 5) { dx = dx / distance; dy = dy / distance; self.x += dx * (self.speed * 1.2); // Slightly faster when chasing self.y += dy * (self.speed * 1.2); } return; } // Regular patrol behavior if (self.patrolPoints.length === 0) { return; } if (self.waitCounter > 0) { self.waitCounter--; return; } var targetPoint = self.patrolPoints[self.currentPatrolIndex]; var dx = targetPoint.x - self.x; var dy = targetPoint.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 5) { // Reached patrol point, wait and then move to next self.waitCounter = self.waitTime; self.currentPatrolIndex = (self.currentPatrolIndex + 1) % self.patrolPoints.length; } else { // Move towards patrol point dx = dx / distance; dy = dy / distance; self.x += dx * self.speed; self.y += dy * self.speed; } }; self.startChasing = function (player) { self.isChasing = true; self.chasedPlayer = player; LK.getSound('detention').play(); }; self.stopChasing = function () { self.isChasing = false; self.chasedPlayer = null; }; return self; }); var Wall = Container.expand(function () { var self = Container.call(this); var wallGraphics = self.attachAsset('wallShape', { anchorX: 0.5, anchorY: 0.5 }); self.checkCollision = function (player) { return self.intersects(player); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 }); /**** * Game Code ****/ // Game state variables game.player = null; game.baldi = null; game.principal = null; game.notebooks = []; game.walls = []; game.doors = []; game.notebooksCollected = 0; game.totalNotebooks = 7; game.isShowingProblem = false; game.currentNotebook = null; game.currentProblemIndex = 0; game.gameState = "playing"; // playing, detention, mathProblem, gameOver, win game.detentionTimer = 0; game.movementDirection = { x: 0, y: 0 }; game.runButtonPressed = false; game.exitDoor = null; // UI Elements var staminaBarBg = LK.getAsset('staminaBarBg', { anchorX: 0, anchorY: 0, x: 30, y: 30 }); var staminaBarFg = LK.getAsset('staminaBarFg', { anchorX: 0, anchorY: 0, x: 30, y: 30, width: 400 // Will be updated based on stamina }); var notebookCountText = new Text2('0/' + game.totalNotebooks + ' Notebooks', { size: 40, fill: 0xFFFFFF }); notebookCountText.anchor.set(0, 0); var problemText = new Text2('', { size: 60, fill: 0xFFFFFF }); problemText.anchor.set(0.5, 0.5); var answerInput = new Text2('', { size: 50, fill: 0xFFFFFF }); answerInput.anchor.set(0.5, 0.5); var promptText = new Text2('Tap to enter your answer', { size: 30, fill: 0xAAAAAA }); promptText.anchor.set(0.5, 0.5); var currentInputText = ''; // Initialize map function initializeMap() { // Create school walls var mapWidth = 2000; var mapHeight = 2600; var wallThickness = 40; // Outer walls createWall(mapWidth / 2, wallThickness / 2, mapWidth, wallThickness); // Top createWall(mapWidth / 2, mapHeight - wallThickness / 2, mapWidth, wallThickness); // Bottom createWall(wallThickness / 2, mapHeight / 2, wallThickness, mapHeight); // Left createWall(mapWidth - wallThickness / 2, mapHeight / 2, wallThickness, mapHeight); // Right // Inner walls - horizontal createWall(mapWidth / 4, mapHeight / 4, mapWidth / 2, wallThickness); createWall(mapWidth * 3 / 4, mapHeight / 4, mapWidth / 2, wallThickness); createWall(mapWidth / 4, mapHeight / 2, mapWidth / 2, wallThickness); createWall(mapWidth * 3 / 4, mapHeight / 2, mapWidth / 2, wallThickness); createWall(mapWidth / 4, mapHeight * 3 / 4, mapWidth / 2, wallThickness); createWall(mapWidth * 3 / 4, mapHeight * 3 / 4, mapWidth / 2, wallThickness); // Inner walls - vertical createWall(mapWidth / 4, mapHeight * 3 / 8, wallThickness, mapHeight / 4); createWall(mapWidth / 2, mapHeight * 3 / 8, wallThickness, mapHeight / 4); createWall(mapWidth * 3 / 4, mapHeight * 3 / 8, wallThickness, mapHeight / 4); createWall(mapWidth / 4, mapHeight * 7 / 8, wallThickness, mapHeight / 4); createWall(mapWidth / 2, mapHeight * 7 / 8, wallThickness, mapHeight / 4); createWall(mapWidth * 3 / 4, mapHeight * 7 / 8, wallThickness, mapHeight / 4); // Create doors createDoor(mapWidth / 2, mapHeight / 4 + 80); createDoor(mapWidth * 3 / 4, mapHeight / 4 + 80); createDoor(mapWidth / 4, mapHeight / 2 + 80); createDoor(mapWidth / 2, mapHeight / 2 + 80); createDoor(mapWidth * 3 / 4, mapHeight * 3 / 4 - 80); // Exit door game.exitDoor = createDoor(mapWidth - 120, mapHeight / 2); game.exitDoor.setAsExit(); // Create notebooks createNotebook(mapWidth / 4 - 100, mapHeight / 4 - 100); createNotebook(mapWidth * 3 / 4 + 100, mapHeight / 4 - 100); createNotebook(mapWidth / 4 - 100, mapHeight / 2 + 100); createNotebook(mapWidth / 2 + 100, mapHeight / 2 - 100); createNotebook(mapWidth * 3 / 4 + 100, mapHeight * 3 / 4 - 100); createNotebook(mapWidth / 4 - 100, mapHeight * 3 / 4 + 100); createNotebook(mapWidth * 3 / 4 + 100, mapHeight - 200); // Create the player game.player = new Player(); game.player.x = 120; game.player.y = 120; game.addChild(game.player); // Create Baldi game.baldi = new Baldi(); game.baldi.x = mapWidth / 2; game.baldi.y = mapHeight / 2; game.addChild(game.baldi); // Create Principal game.principal = new Principal(); game.principal.setPatrolPoints([{ x: mapWidth / 4, y: mapHeight / 4 }, { x: mapWidth * 3 / 4, y: mapHeight / 4 }, { x: mapWidth * 3 / 4, y: mapHeight * 3 / 4 }, { x: mapWidth / 4, y: mapHeight * 3 / 4 }]); game.addChild(game.principal); } function createWall(x, y, width, height) { var wall = new Wall(); wall.x = x; wall.y = y; wall.width = width; wall.height = height; game.walls.push(wall); game.addChild(wall); return wall; } function createDoor(x, y) { var door = new Door(); door.x = x; door.y = y; game.doors.push(door); game.addChild(door); return door; } function createNotebook(x, y) { var notebook = new Notebook(); notebook.x = x; notebook.y = y; notebook.generateProblems(); game.notebooks.push(notebook); game.addChild(notebook); return notebook; } function setupUI() { // Add stamina bar to GUI LK.gui.addChild(staminaBarBg); LK.gui.addChild(staminaBarFg); // Add notebook counter notebookCountText.x = 30; notebookCountText.y = 80; LK.gui.addChild(notebookCountText); // Set up problem UI (not visible initially) problemText.x = 1024; problemText.y = 1200; LK.gui.addChild(problemText); answerInput.x = 1024; answerInput.y = 1300; LK.gui.addChild(answerInput); promptText.x = 1024; promptText.y = 1400; LK.gui.addChild(promptText); // Hide problem UI initially toggleProblemUI(false); } function toggleProblemUI(show) { problemText.visible = show; answerInput.visible = show; promptText.visible = show; game.isShowingProblem = show; } function showNextProblem() { if (!game.currentNotebook) { return; } var problems = game.currentNotebook.problems; if (game.currentProblemIndex >= problems.length) { // All problems answered, collect notebook collectNotebook(); return; } var problem = problems[game.currentProblemIndex]; problemText.setText(problem.question); answerInput.setText(""); currentInputText = ""; toggleProblemUI(true); } function collectNotebook() { if (!game.currentNotebook) { return; } game.currentNotebook.collected = true; game.notebooksCollected++; notebookCountText.setText(game.notebooksCollected + '/' + game.totalNotebooks + ' Notebooks'); LK.getSound('collect').play(); // Check if all notebooks are collected if (game.notebooksCollected >= game.totalNotebooks) { // Unlock exit game.exitDoor.isLocked = false; tween(game.exitDoor, { tint: 0x00ff00 }, { duration: 1000, easing: tween.easeOut }); } toggleProblemUI(false); game.gameState = "playing"; game.currentNotebook = null; } function submitAnswer() { if (!game.currentNotebook) { return; } var problems = game.currentNotebook.problems; var problem = problems[game.currentProblemIndex]; var userAnswer = currentInputText.trim(); var isCorrect = false; if (problem.isTrick) { // Trick questions always wrong isCorrect = false; } else { // Regular math problems var numAnswer = parseInt(userAnswer); isCorrect = !isNaN(numAnswer) && numAnswer === problem.answer; } if (isCorrect) { LK.getSound('correct').play(); } else { LK.getSound('wrong').play(); game.baldi.increaseAnger(); } game.currentProblemIndex++; LK.setTimeout(showNextProblem, 500); } function handlePlayerInput() { if (game.gameState === "mathProblem") { // Handle numeric input for math problems return; } if (game.gameState === "detention") { // No movement in detention game.movementDirection.x = 0; game.movementDirection.y = 0; return; } // Apply movement if (game.movementDirection.x !== 0 || game.movementDirection.y !== 0) { game.player.move(game.movementDirection.x, game.movementDirection.y); } // Update player running state game.player.isRunning = game.runButtonPressed && game.player.stamina > 0; } function checkCollisions() { // Only check collisions if playing if (game.gameState !== "playing") { return; } // Wall collisions for (var i = 0; i < game.walls.length; i++) { var wall = game.walls[i]; if (game.player.collidesWith(wall)) { // Simple bounce back game.player.x -= game.movementDirection.x * game.player.speed * 1.5; game.player.y -= game.movementDirection.y * game.player.speed * 1.5; } } // Door interactions for (var i = 0; i < game.doors.length; i++) { var door = game.doors[i]; if (game.player.collidesWith(door)) { if (door.isExit && !door.isLocked) { // Win condition LK.showYouWin(); return; } } } // Notebook collection for (var i = 0; i < game.notebooks.length; i++) { var notebook = game.notebooks[i]; if (!notebook.collected && game.player.collidesWith(notebook)) { // Start math problem game.currentNotebook = notebook; game.currentProblemIndex = 0; game.gameState = "mathProblem"; showNextProblem(); return; } } // Baldi collision - game over if (game.player.collidesWith(game.baldi)) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } // Principal detection if (game.player.isRunning && !game.principal.isChasing && distanceBetween(game.player, game.principal) < 300) { // Principal spots player running game.principal.startChasing(game.player); } } function putPlayerInDetention() { game.gameState = "detention"; game.detentionTimer = 180; // 3 seconds at 60fps LK.getSound('detention').play(); // Flash player tween(game.player, { alpha: 0.5 }, { duration: 250, easing: tween.easeOut, onFinish: function onFinish() { tween(game.player, { alpha: 1 }, { duration: 250, easing: tween.easeIn }); } }); } function updateDetention() { if (game.gameState !== "detention") { return; } game.detentionTimer--; if (game.detentionTimer <= 0) { game.gameState = "playing"; game.principal.stopChasing(); } } function updateNPCs() { // Update Baldi game.baldi.setTarget(game.player.x, game.player.y); game.baldi.update(); // Update Principal game.principal.update(); } function updateUI() { // Update stamina bar var staminaPercent = game.player.stamina / game.player.maxStamina; staminaBarFg.width = 400 * staminaPercent; // Update stamina bar color based on level if (staminaPercent > 0.6) { staminaBarFg.tint = 0x00ff00; } else if (staminaPercent > 0.3) { staminaBarFg.tint = 0xffff00; } else { staminaBarFg.tint = 0xff0000; } } function distanceBetween(obj1, obj2) { var dx = obj1.x - obj2.x; var dy = obj1.y - obj2.y; return Math.sqrt(dx * dx + dy * dy); } // Initialize game initializeMap(); setupUI(); LK.playMusic('gameMusic'); // Set up input handlers game.down = function (x, y, obj) { // Handle input for math problems if (game.gameState === "mathProblem") { // Check if player tapped on a number var gridSize = 120; var startX = 804; // Start position for number grid (center of screen - grid width/2) var startY = 1450; // Calculate which button was pressed var buttonX = Math.floor((x - startX) / gridSize); var buttonY = Math.floor((y - startY) / gridSize); if (buttonX >= 0 && buttonX < 3 && buttonY >= 0 && buttonY < 4) { // Number buttons 1-9 and special buttons var buttonValue = buttonX + buttonY * 3 + 1; if (buttonValue <= 9) { // Numbers 1-9 currentInputText += buttonValue; } else if (buttonValue === 10) { // Button 10 is "0" currentInputText += "0"; } else if (buttonValue === 11) { // Button 11 is "Backspace" if (currentInputText.length > 0) { currentInputText = currentInputText.substring(0, currentInputText.length - 1); } } else if (buttonValue === 12) { // Button 12 is "Enter/Submit" submitAnswer(); return; } answerInput.setText(currentInputText); } return; } // Handle movement controls var centerX = 1024; var centerY = 2200; var dx = x - centerX; var dy = y - centerY; // Check if this is the run button if (y < 2000 && x > 1700) { game.runButtonPressed = true; return; } // Determine movement direction based on touch position var angle = Math.atan2(dy, dx); // Normalize to 8 directions game.movementDirection.x = 0; game.movementDirection.y = 0; if (angle < -Math.PI * 7 / 8 || angle > Math.PI * 7 / 8) { game.movementDirection.x = -1; // Left } else if (angle < -Math.PI * 5 / 8) { game.movementDirection.x = -1; // Up-left game.movementDirection.y = -1; } else if (angle < -Math.PI * 3 / 8) { game.movementDirection.y = -1; // Up } else if (angle < -Math.PI * 1 / 8) { game.movementDirection.x = 1; // Up-right game.movementDirection.y = -1; } else if (angle < Math.PI * 1 / 8) { game.movementDirection.x = 1; // Right } else if (angle < Math.PI * 3 / 8) { game.movementDirection.x = 1; // Down-right game.movementDirection.y = 1; } else if (angle < Math.PI * 5 / 8) { game.movementDirection.y = 1; // Down } else if (angle < Math.PI * 7 / 8) { game.movementDirection.x = -1; // Down-left game.movementDirection.y = 1; } }; game.up = function (x, y, obj) { // Check if this is the run button being released if (y < 2000 && x > 1700) { game.runButtonPressed = false; return; } // Stop movement when touch is released game.movementDirection.x = 0; game.movementDirection.y = 0; }; game.move = function (x, y, obj) { // Update movement direction if the player is dragging if (game.movementDirection.x !== 0 || game.movementDirection.y !== 0) { game.down(x, y, obj); } }; // Main game update function game.update = function () { handlePlayerInput(); checkCollisions(); updateDetention(); updateNPCs(); updateUI(); // Draw number pad if showing math problem if (game.isShowingProblem) { // This would normally be done using actual UI elements, // but for simplicity, we'll handle it in the update function } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Baldi = Container.expand(function () {
var self = Container.call(this);
var baldiGraphics = self.attachAsset('baldiShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = 2;
self.speed = self.baseSpeed;
self.targetX = 0;
self.targetY = 0;
self.angerLevel = 0;
self.lastRulerSound = 0;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
};
self.update = function () {
// Move towards player
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Normalize direction
dx = dx / distance;
dy = dy / distance;
// Apply movement with current speed
self.x += dx * self.speed;
self.y += dy * self.speed;
// Play ruler slap sound periodically
var currentTime = Date.now();
if (currentTime - self.lastRulerSound > 1500 / (1 + self.angerLevel * 0.2)) {
LK.getSound('ruler').play();
self.lastRulerSound = currentTime;
}
}
};
self.increaseAnger = function () {
self.angerLevel++;
self.speed = self.baseSpeed + self.angerLevel * 0.5;
tween(baldiGraphics, {
tint: 0xff0000
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(baldiGraphics, {
tint: 0xffffff
}, {
duration: 500,
easing: tween.easeIn
});
}
});
};
return self;
});
var Door = Container.expand(function () {
var self = Container.call(this);
var doorGraphics = self.attachAsset('doorShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.isLocked = false;
self.isExit = false;
self.setAsExit = function () {
self.isExit = true;
self.isLocked = true;
doorGraphics.destroy();
var exitDoorGraphics = self.attachAsset('exitDoorShape', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
};
return self;
});
var Notebook = Container.expand(function () {
var self = Container.call(this);
var notebookGraphics = self.attachAsset('notebookShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.problems = [];
self.generateProblems = function () {
self.problems = [];
for (var i = 0; i < 3; i++) {
var difficulty = game.notebooksCollected;
var problem;
if (difficulty < 2) {
// Simple addition problems
var a = Math.floor(Math.random() * 10) + 1;
var b = Math.floor(Math.random() * 10) + 1;
problem = {
question: a + " + " + b + " = ?",
answer: a + b
};
} else if (difficulty < 4) {
// Medium difficulty problems (addition/subtraction)
var a = Math.floor(Math.random() * 20) + 1;
var b = Math.floor(Math.random() * 10) + 1;
var isAddition = Math.random() > 0.5;
if (isAddition) {
problem = {
question: a + " + " + b + " = ?",
answer: a + b
};
} else {
// Ensure a > b for subtraction
if (a < b) {
var temp = a;
a = b;
b = temp;
}
problem = {
question: a + " - " + b + " = ?",
answer: a - b
};
}
} else {
// Hard problems (multiplication, division)
if (Math.random() > 0.5) {
// Multiplication
var a = Math.floor(Math.random() * 12) + 1;
var b = Math.floor(Math.random() * 12) + 1;
problem = {
question: a + " × " + b + " = ?",
answer: a * b
};
} else {
// Division (make sure it's clean division)
var b = Math.floor(Math.random() * 10) + 1;
var result = Math.floor(Math.random() * 10) + 1;
var a = b * result;
problem = {
question: a + " ÷ " + b + " = ?",
answer: result
};
}
}
// Add occasional trick questions in later notebooks
if (difficulty > 3 && Math.random() < 0.2) {
problem = {
question: Math.random() < 0.5 ? "What is Baldi's favorite number?" : "What sound does a bee make?",
answer: "IMPOSSIBLE",
isTrick: true
};
}
self.problems.push(problem);
}
return self.problems;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('playerShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.maxStamina = 100;
self.stamina = self.maxStamina;
self.isRunning = false;
self.lastFootstepTime = 0;
self.move = function (dx, dy) {
var moveSpeed = self.speed;
if (self.isRunning && self.stamina > 0) {
moveSpeed *= 1.8;
self.stamina -= 0.8;
if (self.stamina < 0) {
self.stamina = 0;
}
} else {
// Recover stamina when not running
if (!self.isRunning && self.stamina < self.maxStamina) {
self.stamina += 0.3;
if (self.stamina > self.maxStamina) {
self.stamina = self.maxStamina;
}
}
}
// Normalize diagonal movement
if (dx !== 0 && dy !== 0) {
var length = Math.sqrt(dx * dx + dy * dy);
dx = dx / length;
dy = dy / length;
}
// Apply movement
var newX = self.x + dx * moveSpeed;
var newY = self.y + dy * moveSpeed;
// Wall collision will be checked in the game.update method
self.x = newX;
self.y = newY;
// Play footstep sound periodically while moving
if (dx !== 0 || dy !== 0) {
var currentTime = Date.now();
if (currentTime - self.lastFootstepTime > (self.isRunning ? 250 : 350)) {
LK.getSound('footstep').play();
self.lastFootstepTime = currentTime;
}
}
};
self.collidesWith = function (obj) {
return self.intersects(obj);
};
return self;
});
var Principal = Container.expand(function () {
var self = Container.call(this);
var principalGraphics = self.attachAsset('principalShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.patrolPoints = [];
self.currentPatrolIndex = 0;
self.waitCounter = 0;
self.waitTime = 60; // frames to wait at each patrol point
self.isChasing = false;
self.chasedPlayer = null;
self.detentionDuration = 180; // 3 seconds at 60fps
self.setPatrolPoints = function (points) {
self.patrolPoints = points;
if (points.length > 0) {
self.x = points[0].x;
self.y = points[0].y;
}
return self;
};
self.update = function () {
if (self.isChasing && self.chasedPlayer) {
// Chase the running player
var dx = self.chasedPlayer.x - self.x;
var dy = self.chasedPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
// Caught the player - put in detention
self.isChasing = false;
game.putPlayerInDetention();
return;
}
// Move towards player
if (distance > 5) {
dx = dx / distance;
dy = dy / distance;
self.x += dx * (self.speed * 1.2); // Slightly faster when chasing
self.y += dy * (self.speed * 1.2);
}
return;
}
// Regular patrol behavior
if (self.patrolPoints.length === 0) {
return;
}
if (self.waitCounter > 0) {
self.waitCounter--;
return;
}
var targetPoint = self.patrolPoints[self.currentPatrolIndex];
var dx = targetPoint.x - self.x;
var dy = targetPoint.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
// Reached patrol point, wait and then move to next
self.waitCounter = self.waitTime;
self.currentPatrolIndex = (self.currentPatrolIndex + 1) % self.patrolPoints.length;
} else {
// Move towards patrol point
dx = dx / distance;
dy = dy / distance;
self.x += dx * self.speed;
self.y += dy * self.speed;
}
};
self.startChasing = function (player) {
self.isChasing = true;
self.chasedPlayer = player;
LK.getSound('detention').play();
};
self.stopChasing = function () {
self.isChasing = false;
self.chasedPlayer = null;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wallShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.checkCollision = function (player) {
return self.intersects(player);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x333333
});
/****
* Game Code
****/
// Game state variables
game.player = null;
game.baldi = null;
game.principal = null;
game.notebooks = [];
game.walls = [];
game.doors = [];
game.notebooksCollected = 0;
game.totalNotebooks = 7;
game.isShowingProblem = false;
game.currentNotebook = null;
game.currentProblemIndex = 0;
game.gameState = "playing"; // playing, detention, mathProblem, gameOver, win
game.detentionTimer = 0;
game.movementDirection = {
x: 0,
y: 0
};
game.runButtonPressed = false;
game.exitDoor = null;
// UI Elements
var staminaBarBg = LK.getAsset('staminaBarBg', {
anchorX: 0,
anchorY: 0,
x: 30,
y: 30
});
var staminaBarFg = LK.getAsset('staminaBarFg', {
anchorX: 0,
anchorY: 0,
x: 30,
y: 30,
width: 400 // Will be updated based on stamina
});
var notebookCountText = new Text2('0/' + game.totalNotebooks + ' Notebooks', {
size: 40,
fill: 0xFFFFFF
});
notebookCountText.anchor.set(0, 0);
var problemText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
problemText.anchor.set(0.5, 0.5);
var answerInput = new Text2('', {
size: 50,
fill: 0xFFFFFF
});
answerInput.anchor.set(0.5, 0.5);
var promptText = new Text2('Tap to enter your answer', {
size: 30,
fill: 0xAAAAAA
});
promptText.anchor.set(0.5, 0.5);
var currentInputText = '';
// Initialize map
function initializeMap() {
// Create school walls
var mapWidth = 2000;
var mapHeight = 2600;
var wallThickness = 40;
// Outer walls
createWall(mapWidth / 2, wallThickness / 2, mapWidth, wallThickness); // Top
createWall(mapWidth / 2, mapHeight - wallThickness / 2, mapWidth, wallThickness); // Bottom
createWall(wallThickness / 2, mapHeight / 2, wallThickness, mapHeight); // Left
createWall(mapWidth - wallThickness / 2, mapHeight / 2, wallThickness, mapHeight); // Right
// Inner walls - horizontal
createWall(mapWidth / 4, mapHeight / 4, mapWidth / 2, wallThickness);
createWall(mapWidth * 3 / 4, mapHeight / 4, mapWidth / 2, wallThickness);
createWall(mapWidth / 4, mapHeight / 2, mapWidth / 2, wallThickness);
createWall(mapWidth * 3 / 4, mapHeight / 2, mapWidth / 2, wallThickness);
createWall(mapWidth / 4, mapHeight * 3 / 4, mapWidth / 2, wallThickness);
createWall(mapWidth * 3 / 4, mapHeight * 3 / 4, mapWidth / 2, wallThickness);
// Inner walls - vertical
createWall(mapWidth / 4, mapHeight * 3 / 8, wallThickness, mapHeight / 4);
createWall(mapWidth / 2, mapHeight * 3 / 8, wallThickness, mapHeight / 4);
createWall(mapWidth * 3 / 4, mapHeight * 3 / 8, wallThickness, mapHeight / 4);
createWall(mapWidth / 4, mapHeight * 7 / 8, wallThickness, mapHeight / 4);
createWall(mapWidth / 2, mapHeight * 7 / 8, wallThickness, mapHeight / 4);
createWall(mapWidth * 3 / 4, mapHeight * 7 / 8, wallThickness, mapHeight / 4);
// Create doors
createDoor(mapWidth / 2, mapHeight / 4 + 80);
createDoor(mapWidth * 3 / 4, mapHeight / 4 + 80);
createDoor(mapWidth / 4, mapHeight / 2 + 80);
createDoor(mapWidth / 2, mapHeight / 2 + 80);
createDoor(mapWidth * 3 / 4, mapHeight * 3 / 4 - 80);
// Exit door
game.exitDoor = createDoor(mapWidth - 120, mapHeight / 2);
game.exitDoor.setAsExit();
// Create notebooks
createNotebook(mapWidth / 4 - 100, mapHeight / 4 - 100);
createNotebook(mapWidth * 3 / 4 + 100, mapHeight / 4 - 100);
createNotebook(mapWidth / 4 - 100, mapHeight / 2 + 100);
createNotebook(mapWidth / 2 + 100, mapHeight / 2 - 100);
createNotebook(mapWidth * 3 / 4 + 100, mapHeight * 3 / 4 - 100);
createNotebook(mapWidth / 4 - 100, mapHeight * 3 / 4 + 100);
createNotebook(mapWidth * 3 / 4 + 100, mapHeight - 200);
// Create the player
game.player = new Player();
game.player.x = 120;
game.player.y = 120;
game.addChild(game.player);
// Create Baldi
game.baldi = new Baldi();
game.baldi.x = mapWidth / 2;
game.baldi.y = mapHeight / 2;
game.addChild(game.baldi);
// Create Principal
game.principal = new Principal();
game.principal.setPatrolPoints([{
x: mapWidth / 4,
y: mapHeight / 4
}, {
x: mapWidth * 3 / 4,
y: mapHeight / 4
}, {
x: mapWidth * 3 / 4,
y: mapHeight * 3 / 4
}, {
x: mapWidth / 4,
y: mapHeight * 3 / 4
}]);
game.addChild(game.principal);
}
function createWall(x, y, width, height) {
var wall = new Wall();
wall.x = x;
wall.y = y;
wall.width = width;
wall.height = height;
game.walls.push(wall);
game.addChild(wall);
return wall;
}
function createDoor(x, y) {
var door = new Door();
door.x = x;
door.y = y;
game.doors.push(door);
game.addChild(door);
return door;
}
function createNotebook(x, y) {
var notebook = new Notebook();
notebook.x = x;
notebook.y = y;
notebook.generateProblems();
game.notebooks.push(notebook);
game.addChild(notebook);
return notebook;
}
function setupUI() {
// Add stamina bar to GUI
LK.gui.addChild(staminaBarBg);
LK.gui.addChild(staminaBarFg);
// Add notebook counter
notebookCountText.x = 30;
notebookCountText.y = 80;
LK.gui.addChild(notebookCountText);
// Set up problem UI (not visible initially)
problemText.x = 1024;
problemText.y = 1200;
LK.gui.addChild(problemText);
answerInput.x = 1024;
answerInput.y = 1300;
LK.gui.addChild(answerInput);
promptText.x = 1024;
promptText.y = 1400;
LK.gui.addChild(promptText);
// Hide problem UI initially
toggleProblemUI(false);
}
function toggleProblemUI(show) {
problemText.visible = show;
answerInput.visible = show;
promptText.visible = show;
game.isShowingProblem = show;
}
function showNextProblem() {
if (!game.currentNotebook) {
return;
}
var problems = game.currentNotebook.problems;
if (game.currentProblemIndex >= problems.length) {
// All problems answered, collect notebook
collectNotebook();
return;
}
var problem = problems[game.currentProblemIndex];
problemText.setText(problem.question);
answerInput.setText("");
currentInputText = "";
toggleProblemUI(true);
}
function collectNotebook() {
if (!game.currentNotebook) {
return;
}
game.currentNotebook.collected = true;
game.notebooksCollected++;
notebookCountText.setText(game.notebooksCollected + '/' + game.totalNotebooks + ' Notebooks');
LK.getSound('collect').play();
// Check if all notebooks are collected
if (game.notebooksCollected >= game.totalNotebooks) {
// Unlock exit
game.exitDoor.isLocked = false;
tween(game.exitDoor, {
tint: 0x00ff00
}, {
duration: 1000,
easing: tween.easeOut
});
}
toggleProblemUI(false);
game.gameState = "playing";
game.currentNotebook = null;
}
function submitAnswer() {
if (!game.currentNotebook) {
return;
}
var problems = game.currentNotebook.problems;
var problem = problems[game.currentProblemIndex];
var userAnswer = currentInputText.trim();
var isCorrect = false;
if (problem.isTrick) {
// Trick questions always wrong
isCorrect = false;
} else {
// Regular math problems
var numAnswer = parseInt(userAnswer);
isCorrect = !isNaN(numAnswer) && numAnswer === problem.answer;
}
if (isCorrect) {
LK.getSound('correct').play();
} else {
LK.getSound('wrong').play();
game.baldi.increaseAnger();
}
game.currentProblemIndex++;
LK.setTimeout(showNextProblem, 500);
}
function handlePlayerInput() {
if (game.gameState === "mathProblem") {
// Handle numeric input for math problems
return;
}
if (game.gameState === "detention") {
// No movement in detention
game.movementDirection.x = 0;
game.movementDirection.y = 0;
return;
}
// Apply movement
if (game.movementDirection.x !== 0 || game.movementDirection.y !== 0) {
game.player.move(game.movementDirection.x, game.movementDirection.y);
}
// Update player running state
game.player.isRunning = game.runButtonPressed && game.player.stamina > 0;
}
function checkCollisions() {
// Only check collisions if playing
if (game.gameState !== "playing") {
return;
}
// Wall collisions
for (var i = 0; i < game.walls.length; i++) {
var wall = game.walls[i];
if (game.player.collidesWith(wall)) {
// Simple bounce back
game.player.x -= game.movementDirection.x * game.player.speed * 1.5;
game.player.y -= game.movementDirection.y * game.player.speed * 1.5;
}
}
// Door interactions
for (var i = 0; i < game.doors.length; i++) {
var door = game.doors[i];
if (game.player.collidesWith(door)) {
if (door.isExit && !door.isLocked) {
// Win condition
LK.showYouWin();
return;
}
}
}
// Notebook collection
for (var i = 0; i < game.notebooks.length; i++) {
var notebook = game.notebooks[i];
if (!notebook.collected && game.player.collidesWith(notebook)) {
// Start math problem
game.currentNotebook = notebook;
game.currentProblemIndex = 0;
game.gameState = "mathProblem";
showNextProblem();
return;
}
}
// Baldi collision - game over
if (game.player.collidesWith(game.baldi)) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
// Principal detection
if (game.player.isRunning && !game.principal.isChasing && distanceBetween(game.player, game.principal) < 300) {
// Principal spots player running
game.principal.startChasing(game.player);
}
}
function putPlayerInDetention() {
game.gameState = "detention";
game.detentionTimer = 180; // 3 seconds at 60fps
LK.getSound('detention').play();
// Flash player
tween(game.player, {
alpha: 0.5
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game.player, {
alpha: 1
}, {
duration: 250,
easing: tween.easeIn
});
}
});
}
function updateDetention() {
if (game.gameState !== "detention") {
return;
}
game.detentionTimer--;
if (game.detentionTimer <= 0) {
game.gameState = "playing";
game.principal.stopChasing();
}
}
function updateNPCs() {
// Update Baldi
game.baldi.setTarget(game.player.x, game.player.y);
game.baldi.update();
// Update Principal
game.principal.update();
}
function updateUI() {
// Update stamina bar
var staminaPercent = game.player.stamina / game.player.maxStamina;
staminaBarFg.width = 400 * staminaPercent;
// Update stamina bar color based on level
if (staminaPercent > 0.6) {
staminaBarFg.tint = 0x00ff00;
} else if (staminaPercent > 0.3) {
staminaBarFg.tint = 0xffff00;
} else {
staminaBarFg.tint = 0xff0000;
}
}
function distanceBetween(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Initialize game
initializeMap();
setupUI();
LK.playMusic('gameMusic');
// Set up input handlers
game.down = function (x, y, obj) {
// Handle input for math problems
if (game.gameState === "mathProblem") {
// Check if player tapped on a number
var gridSize = 120;
var startX = 804; // Start position for number grid (center of screen - grid width/2)
var startY = 1450;
// Calculate which button was pressed
var buttonX = Math.floor((x - startX) / gridSize);
var buttonY = Math.floor((y - startY) / gridSize);
if (buttonX >= 0 && buttonX < 3 && buttonY >= 0 && buttonY < 4) {
// Number buttons 1-9 and special buttons
var buttonValue = buttonX + buttonY * 3 + 1;
if (buttonValue <= 9) {
// Numbers 1-9
currentInputText += buttonValue;
} else if (buttonValue === 10) {
// Button 10 is "0"
currentInputText += "0";
} else if (buttonValue === 11) {
// Button 11 is "Backspace"
if (currentInputText.length > 0) {
currentInputText = currentInputText.substring(0, currentInputText.length - 1);
}
} else if (buttonValue === 12) {
// Button 12 is "Enter/Submit"
submitAnswer();
return;
}
answerInput.setText(currentInputText);
}
return;
}
// Handle movement controls
var centerX = 1024;
var centerY = 2200;
var dx = x - centerX;
var dy = y - centerY;
// Check if this is the run button
if (y < 2000 && x > 1700) {
game.runButtonPressed = true;
return;
}
// Determine movement direction based on touch position
var angle = Math.atan2(dy, dx);
// Normalize to 8 directions
game.movementDirection.x = 0;
game.movementDirection.y = 0;
if (angle < -Math.PI * 7 / 8 || angle > Math.PI * 7 / 8) {
game.movementDirection.x = -1; // Left
} else if (angle < -Math.PI * 5 / 8) {
game.movementDirection.x = -1; // Up-left
game.movementDirection.y = -1;
} else if (angle < -Math.PI * 3 / 8) {
game.movementDirection.y = -1; // Up
} else if (angle < -Math.PI * 1 / 8) {
game.movementDirection.x = 1; // Up-right
game.movementDirection.y = -1;
} else if (angle < Math.PI * 1 / 8) {
game.movementDirection.x = 1; // Right
} else if (angle < Math.PI * 3 / 8) {
game.movementDirection.x = 1; // Down-right
game.movementDirection.y = 1;
} else if (angle < Math.PI * 5 / 8) {
game.movementDirection.y = 1; // Down
} else if (angle < Math.PI * 7 / 8) {
game.movementDirection.x = -1; // Down-left
game.movementDirection.y = 1;
}
};
game.up = function (x, y, obj) {
// Check if this is the run button being released
if (y < 2000 && x > 1700) {
game.runButtonPressed = false;
return;
}
// Stop movement when touch is released
game.movementDirection.x = 0;
game.movementDirection.y = 0;
};
game.move = function (x, y, obj) {
// Update movement direction if the player is dragging
if (game.movementDirection.x !== 0 || game.movementDirection.y !== 0) {
game.down(x, y, obj);
}
};
// Main game update function
game.update = function () {
handlePlayerInput();
checkCollisions();
updateDetention();
updateNPCs();
updateUI();
// Draw number pad if showing math problem
if (game.isShowingProblem) {
// This would normally be done using actual UI elements,
// but for simplicity, we'll handle it in the update function
}
};