/**** * Classes ****/ // ProfesorMonster class: AI-driven monster that chases the student var ProfesorMonster = Container.expand(function () { var self = Container.call(this); // Attach profesor asset var profesorSprite = self.attachAsset('profesor', { anchorX: 0.5, anchorY: 0.5 }); // Track last position for event logic self.lastX = self.x; self.lastY = self.y; self.lastWasIntersecting = false; // Update method: chase the student self.update = function () { // Store last position self.lastX = self.x; self.lastY = self.y; // Immobilize profesor if student is touching the door (popup active) if (typeof showMathQuestion !== "undefined" && showMathQuestion || typeof student !== "undefined" && typeof student.lastWasIntersectingDoor !== "undefined" && student.lastWasIntersectingDoor) { // Profesor stays still, do not move return; } // Use global student variable if (typeof student === "undefined" || !student) return; // Simple AI: move towards the student var dx = student.x - self.x; var dy = student.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 6; // profesor speed, now much slower than student if (dist > speed) { // Calculate intended new position var nextX = self.x + dx / dist * speed; var nextY = self.y + dy / dist * speed; var blocked = false; // Level 2: block profesor on obstacles if (typeof currentLevel !== "undefined" && currentLevel === 2 && typeof game !== "undefined" && game.level2Obstacles) { for (var i = 0; i < game.level2Obstacles.length; i++) { var obs = game.level2Obstacles[i]; // Simulate move: temporarily set position, check collision, revert var oldX = self.x, oldY = self.y; self.x = nextX; self.y = nextY; if (self.intersects(obs)) { blocked = true; } self.x = oldX; self.y = oldY; if (blocked) break; } } // Level 2: also block corridor walls if (!blocked && typeof currentLevel !== "undefined" && currentLevel === 2) { var corridorHeight = 520; var corridorY = (2732 - corridorHeight) / 2; var corridorTop = corridorY; var corridorBottom = corridorY + corridorHeight; var leftWall = 40; var rightWall = 2048 - 40; var profesorTop = nextY - 80; var profesorBottom = nextY + 80; var profesorLeft = nextX - 80; var profesorRight = nextX + 80; if (profesorTop < corridorTop || profesorBottom > corridorBottom || profesorLeft < leftWall || profesorRight > rightWall) { blocked = true; } } // Level 3: block profesor on rocks and map boundaries if (!blocked && typeof currentLevel !== "undefined" && currentLevel === 3 && typeof game !== "undefined" && game.level3Obstacles) { for (var i = 0; i < game.level3Obstacles.length; i++) { var rock = game.level3Obstacles[i]; var oldX = self.x, oldY = self.y; self.x = nextX; self.y = nextY; if (self.intersects(rock)) { blocked = true; } self.x = oldX; self.y = oldY; if (blocked) break; } // Map boundaries for profesor in level 3 var leftWall = 40; var rightWall = 2048 - 40; var topWall = 40; var bottomWall = 2732 - 40; var profesorLeft = nextX - 80; var profesorRight = nextX + 80; var profesorTop = nextY - 80; var profesorBottom = nextY + 80; if (profesorLeft < leftWall || profesorRight > rightWall || profesorTop < topWall || profesorBottom > bottomWall) { blocked = true; } } // Level 4: block profesor on maze walls and soccer balls and map boundaries if (!blocked && typeof currentLevel !== "undefined" && currentLevel === 4 && typeof game !== "undefined") { // Maze walls if (game.level4MazeWalls) { for (var i = 0; i < game.level4MazeWalls.length; i++) { var wall = game.level4MazeWalls[i]; var oldX = self.x, oldY = self.y; self.x = nextX; self.y = nextY; if (self.intersects(wall)) { blocked = true; } self.x = oldX; self.y = oldY; if (blocked) break; } } // Soccer balls collision removed for level 4 // Map boundaries for profesor in level 4 var leftWall = 40; var rightWall = 2048 - 40; var topWall = 40; var bottomWall = 2732 - 40; var profesorLeft = nextX - 80; var profesorRight = nextX + 80; var profesorTop = nextY - 80; var profesorBottom = nextY + 80; if (profesorLeft < leftWall || profesorRight > rightWall || profesorTop < topWall || profesorBottom > bottomWall) { blocked = true; } } // If not blocked, move if (!blocked) { self.x = nextX; self.y = nextY; } } else { self.x = student.x; self.y = student.y; } }; return self; }); // Student class: controllable player character var Student = Container.expand(function () { var self = Container.call(this); // Attach student asset var studentSprite = self.attachAsset('student', { anchorX: 0.5, anchorY: 0.5 }); // Initial target position is current position self.targetX = 300; self.targetY = 2732 / 2; // Track last position for event logic self.lastX = self.x; self.lastY = self.y; // Update method: move towards target position self.update = function () { // Store last position self.lastX = self.x; self.lastY = self.y; // Move towards targetX, targetY var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 18; // pixels per frame if (dist > speed) { self.x += dx / dist * speed; self.y += dy / dist * speed; } else { self.x = self.targetX; self.y = self.targetY; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Level system // Rocky park floor for level 3 (must be present in asset library) var currentLevel = 1; var maxLevel = 8; // Now 8 levels (added sala de computo) // --- START SCREEN LOGIC --- var startScreenOverlay = null; var gameStarted = false; function showStartScreen() { if (startScreenOverlay) return; startScreenOverlay = new Container(); // Only a single prominent JUGAR button, centered var playBtn = LK.getAsset('furniture', { width: 600, height: 220, color: 0x83de44, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1200 }); playBtn.interactive = true; playBtn.buttonMode = true; startScreenOverlay.addChild(playBtn); var playText = new Text2("JUGAR", { size: 150, fill: 0x000000 }); playText.anchor.set(0.5, 0.5); playText.x = playBtn.x; playText.y = playBtn.y; startScreenOverlay.addChild(playText); playBtn.down = function () { if (startScreenOverlay && startScreenOverlay.parent) { startScreenOverlay.parent.removeChild(startScreenOverlay); } startScreenOverlay = null; gameStarted = true; setupLevel(currentLevel); }; // Add overlay to game (not GUI, so it blocks input) game.addChild(startScreenOverlay); } // Show start screen on load LK.setTimeout(function () { showStartScreen(); }, 10); // Helper to clear all children except joystick and overlays function clearGameObjects() { // Remove all children except overlays and joystick for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; // Don't remove overlays or joystick (which are in LK.gui) game.removeChild(child); } // Clear level 2 obstacles array if present if (game.level2Obstacles) { game.level2Obstacles.length = 0; } } // Helper to setup a level function setupLevel(level) { clearGameObjects(); // Main floor (covers the whole map) - different for each level var floorAssetId; if (level === 1) { floorAssetId = 'floor'; // Default school floor } else if (level === 2) { floorAssetId = 'floor'; // Piso normal para el nivel 2 } else if (level === 3) { floorAssetId = 'floor_rocky'; // Use a rocky park floor for level 3 } else if (level === 4) { floorAssetId = 'floor_rocky'; // Piso de concreto para el nivel 4 } // Only add a floor if floorAssetId is defined (skip for level 5) if (typeof floorAssetId !== "undefined") { var floor = LK.getAsset(floorAssetId, { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(floor); } // Top and bottom walls (main corridor) var wallTop, wallBottom, wallLeft, wallRight; if (level === 3) { // Level 3: Rocky park, open area with rocks as obstacles and a new door position // Remove corridor walls, use only map boundaries wallTop = LK.getAsset('wall', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); wallBottom = LK.getAsset('wall', { x: 0, y: 2732 - 40, anchorX: 0, anchorY: 0 }); wallLeft = LK.getAsset('wall', { width: 40, height: 2732, x: 0, y: 0, anchorX: 0, anchorY: 0 }); wallRight = LK.getAsset('wall', { width: 40, height: 2732, x: 2048 - 40, y: 0, anchorX: 0, anchorY: 0 }); } else if (level === 2) { // Narrow hallway: corridor is 520px tall, centered vertically var corridorHeight = 520; var corridorY = (2732 - corridorHeight) / 2; wallTop = LK.getAsset('wall', { x: 0, y: corridorY - 40, anchorX: 0, anchorY: 0 }); wallBottom = LK.getAsset('wall', { x: 0, y: corridorY + corridorHeight, anchorX: 0, anchorY: 0 }); // Left and right walls (vertical boundaries) wallLeft = LK.getAsset('wall', { width: 40, height: corridorHeight, x: 0, y: corridorY, anchorX: 0, anchorY: 0 }); wallRight = LK.getAsset('wall', { width: 40, height: corridorHeight, x: 2048 - 40, y: corridorY, anchorX: 0, anchorY: 0 }); } else { wallTop = LK.getAsset('wall', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); wallBottom = LK.getAsset('wall', { x: 0, y: 2732 - 40, anchorX: 0, anchorY: 0 }); wallLeft = LK.getAsset('wall', { width: 40, height: 2732, x: 0, y: 0, anchorX: 0, anchorY: 0 }); wallRight = LK.getAsset('wall', { width: 40, height: 2732, x: 2048 - 40, y: 0, anchorX: 0, anchorY: 0 }); } game.addChild(wallTop); game.addChild(wallBottom); game.addChild(wallLeft); game.addChild(wallRight); // Add door (exit) for each level if (level === 1) { door = LK.getAsset('door', { x: 2048 - 200, y: 2200, anchorX: 0, anchorY: 0 }); } else if (level === 2) { // Level 2: Hallway with open doors as obstacles, exit at the end, profesor behind // Place the exit door at the far right end of the hallway, centered in corridor var corridorHeight = 520; var corridorY = (2732 - corridorHeight) / 2; door = LK.getAsset('door', { x: 2048 - 200, y: corridorY + corridorHeight / 2 - 200, anchorX: 0, anchorY: 0 }); // Add open doors as obstacles along the hallway // We'll use furniture assets as open doors (obstacles) var numObstacles = 3; var spacing = (2048 - 400) / (numObstacles + 1); for (var i = 1; i <= numObstacles; i++) { var obsX = i * spacing + 100; // Obstacles alternate above and below the center line, but always inside the corridor var obsY; if (i % 2 === 0) { obsY = corridorY + 120; } else { obsY = corridorY + corridorHeight - 120; } var obstacle = LK.getAsset('furniture', { width: 180, height: 60, anchorX: 0.5, anchorY: 0.5, x: obsX, y: obsY }); game.addChild(obstacle); // Store obstacles for collision in global array if (!game.level2Obstacles) game.level2Obstacles = []; game.level2Obstacles.push(obstacle); } } // Level 3: Add rocky obstacles and new door position if (level === 3) { // Place the exit door at the top right corner of the park door = LK.getAsset('door', { x: 2048 - 320, y: 180, anchorX: 0, anchorY: 0 }); // Add rocky obstacles (use 'furniture' asset as rocks, but with different positions and sizes) var rocks = [{ x: 600, y: 700, w: 180, h: 120 }, { x: 1200, y: 500, w: 220, h: 100 }, { x: 900, y: 1400, w: 160, h: 160 }, { x: 1500, y: 900, w: 200, h: 120 }, { x: 400, y: 2000, w: 180, h: 120 }]; if (!game.level3Obstacles) game.level3Obstacles = []; game.level3Obstacles.length = 0; for (var i = 0; i < rocks.length; i++) { var r = rocks[i]; var rock = LK.getAsset('furniture', { width: r.w, height: r.h, anchorX: 0.5, anchorY: 0.5, x: r.x, y: r.y }); game.addChild(rock); game.level3Obstacles.push(rock); } } // Level 4: Add maze, soccer balls, and door if (level === 4) { // Place the exit door at the top right of the maze door = LK.getAsset('door', { x: 2048 - 320, y: 180, anchorX: 0, anchorY: 0 }); // Maze walls (using 'wall' asset, horizontal and vertical) // Each wall: {x, y, width, height} // Easy maze: outer boundaries plus a few simple interior walls var mazeWalls = [ // Outer boundaries { x: 0, y: 0, width: 2048, height: 40 }, // top { x: 0, y: 2732 - 40, width: 2048, height: 40 }, // bottom { x: 0, y: 0, width: 40, height: 2732 }, // left { x: 2048 - 40, y: 0, width: 40, height: 2732 }, // right // A few easy interior walls (horizontal and vertical, leaving wide passages) { x: 400, y: 600, width: 1200, height: 40 }, // horizontal, upper { x: 400, y: 1500, width: 1200, height: 40 }, // horizontal, lower { x: 800, y: 900, width: 40, height: 600 }, // vertical, left { x: 1200, y: 1200, width: 40, height: 600 } // vertical, right ]; if (!game.level4MazeWalls) game.level4MazeWalls = []; game.level4MazeWalls.length = 0; for (var i = 0; i < mazeWalls.length; i++) { var mw = mazeWalls[i]; var wall = LK.getAsset('wall', { width: mw.width, height: mw.height, x: mw.x, y: mw.y, anchorX: 0, anchorY: 0 }); game.addChild(wall); game.level4MazeWalls.push(wall); } // Soccer balls as obstacles removed for level 4 if (!game.level4SoccerBalls) game.level4SoccerBalls = []; game.level4SoccerBalls.length = 0; } else if (level === 5) { // Level 5: Adivina la puerta correcta (guess the correct door) // Add floor for level 5 var floor = LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(floor); // Three doors, only one is correct if (!game.level5Doors) game.level5Doors = []; game.level5Doors.length = 0; // Place three doors spaced horizontally at the top of the map var doorY = 300; var doorSpacing = 500; var firstDoorX = 524; // Centered: (2048 - (3*240 + 2*500))/2 = 524 var correctDoorIndex = Math.floor(Math.random() * 3); // Random correct door for (var i = 0; i < 3; i++) { var d = LK.getAsset('door', { x: firstDoorX + i * (240 + doorSpacing), y: doorY, anchorX: 0, anchorY: 0 }); d.isCorrectDoor = i === correctDoorIndex; game.addChild(d); game.level5Doors.push(d); } // Add centered text above the doors in level 5 var chooseText = new Text2("Escoje la puerta correcta", { size: 120, fill: 0xFFF700 }); chooseText.anchor.set(0.5, 0.5); chooseText.x = 2048 / 2; chooseText.y = doorY - 80; game.addChild(chooseText); // No maze walls, just outer boundaries var wallTop = LK.getAsset('wall', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallBottom = LK.getAsset('wall', { x: 0, y: 2732 - 40, anchorX: 0, anchorY: 0 }); var wallLeft = LK.getAsset('wall', { width: 40, height: 2732, x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallRight = LK.getAsset('wall', { width: 40, height: 2732, x: 2048 - 40, y: 0, anchorX: 0, anchorY: 0 }); game.addChild(wallTop); game.addChild(wallBottom); game.addChild(wallLeft); game.addChild(wallRight); // Add profesor for level 5 (will chase the student) } else if (level === 6) { // Nivel 6: Patio con UN obstáculo móvil y solo una puerta de salida var floor = LK.getAsset('floor_placa', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(floor); // Solo una puerta en la parte superior, es la salida if (!game.level6Doors) game.level6Doors = []; game.level6Doors.length = 0; var doorY = 200; var doorX = 1024 - 120; // Centrada horizontalmente (2048/2 - door width/2) var d = LK.getAsset('door', { x: doorX, y: doorY, anchorX: 0, anchorY: 0 }); d.isCorrectDoor = true; game.addChild(d); game.level6Doors.push(d); // Texto de pista var chooseText6 = new Text2("¡Encuentra la puerta de salida!", { size: 120, fill: 0xFFF700 }); chooseText6.anchor.set(0.5, 0.5); chooseText6.x = 2048 / 2; chooseText6.y = doorY - 80; game.addChild(chooseText6); // Paredes exteriores var wallTop = LK.getAsset('wall', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallBottom = LK.getAsset('wall', { x: 0, y: 2732 - 40, anchorX: 0, anchorY: 0 }); var wallLeft = LK.getAsset('wall', { width: 40, height: 2732, x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallRight = LK.getAsset('wall', { width: 40, height: 2732, x: 2048 - 40, y: 0, anchorX: 0, anchorY: 0 }); game.addChild(wallTop); game.addChild(wallBottom); game.addChild(wallLeft); game.addChild(wallRight); // Obstáculo móvil (solo 1 bloque que se mueve horizontalmente) if (!game.level6MovingObstacles) game.level6MovingObstacles = []; game.level6MovingObstacles.length = 0; var obsY = [1400]; // Solo un obstáculo, centrado verticalmente for (var i = 0; i < 1; i++) { var obs = LK.getAsset('furniture', { width: 320, height: 80, anchorX: 0.5, anchorY: 0.5, x: 1024, y: obsY[i] }); obs._moveDir = 1; obs._moveMin = 300; obs._moveMax = 2048 - 300; obs._moveSpeed = 10; game.addChild(obs); game.level6MovingObstacles.push(obs); } } else if (level === 7) { // Nivel 7: Laser room - dodge moving laser beams and reach the exit door var floor = LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(floor); // Outer walls var wallTop = LK.getAsset('wall', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallBottom = LK.getAsset('wall', { x: 0, y: 2732 - 40, anchorX: 0, anchorY: 0 }); var wallLeft = LK.getAsset('wall', { width: 40, height: 2732, x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallRight = LK.getAsset('wall', { width: 40, height: 2732, x: 2048 - 40, y: 0, anchorX: 0, anchorY: 0 }); game.addChild(wallTop); game.addChild(wallBottom); game.addChild(wallLeft); game.addChild(wallRight); // Exit door at the top center if (!game.level7Door) game.level7Door = null; var doorY = 120; var doorX = 2048 / 2 - 120; var d7 = LK.getAsset('door', { x: doorX, y: doorY, anchorX: 0, anchorY: 0 }); d7.isCorrectDoor = true; game.addChild(d7); game.level7Door = d7; // Laser beams (moving obstacles) if (!game.level7Lasers) game.level7Lasers = []; game.level7Lasers.length = 0; // Each laser: {x, y, w, h, vertical, min, max, speed, dir} var lasers = [ // Horizontal lasers { x: 300, y: 700, w: 1448, h: 30, vertical: false, min: 700, max: 1200, speed: 8, dir: 1 }, { x: 300, y: 1800, w: 1448, h: 30, vertical: false, min: 1400, max: 2100, speed: 10, dir: -1 }, // Vertical lasers { x: 600, y: 400, w: 30, h: 1000, vertical: true, min: 600, max: 1400, speed: 7, dir: 1 }, { x: 1400, y: 1600, w: 30, h: 900, vertical: true, min: 900, max: 2100, speed: 9, dir: -1 }]; for (var i = 0; i < lasers.length; i++) { var l = lasers[i]; var laser = LK.getAsset('wall', { width: l.w, height: l.h, x: l.x, y: l.y, anchorX: 0, anchorY: 0 }); laser._isLaser = true; laser._vertical = l.vertical; laser._moveMin = l.min; laser._moveMax = l.max; laser._moveSpeed = l.speed; laser._moveDir = l.dir; game.addChild(laser); game.level7Lasers.push(laser); } // Instruction text var instrText = new Text2("¡Esquiva los rayos láser y escapa por la puerta!", { size: 110, fill: 0xFF2222 }); instrText.anchor.set(0.5, 0.5); instrText.x = 2048 / 2; instrText.y = 320; game.addChild(instrText); } else if (level === 8) { // Nivel 8: Sala de cómputo con escritorios como obstáculos y una sola puerta de salida var floor = LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(floor); // Paredes exteriores var wallTop = LK.getAsset('wall', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallBottom = LK.getAsset('wall', { x: 0, y: 2732 - 40, anchorX: 0, anchorY: 0 }); var wallLeft = LK.getAsset('wall', { width: 40, height: 2732, x: 0, y: 0, anchorX: 0, anchorY: 0 }); var wallRight = LK.getAsset('wall', { width: 40, height: 2732, x: 2048 - 40, y: 0, anchorX: 0, anchorY: 0 }); game.addChild(wallTop); game.addChild(wallBottom); game.addChild(wallLeft); game.addChild(wallRight); // Puerta de salida en la parte superior derecha if (!game.level8Door) game.level8Door = null; var doorY = 120; var doorX = 2048 - 320; var d8 = LK.getAsset('door', { x: doorX, y: doorY, anchorX: 0, anchorY: 0 }); d8.isCorrectDoor = true; game.addChild(d8); game.level8Door = d8; // Escritorios como obstáculos (usando 'furniture') if (!game.level8Desks) game.level8Desks = []; game.level8Desks.length = 0; // Distribuir escritorios en filas y columnas, dejando pasillos var deskRows = 3; var deskCols = 4; var deskW = 180; var deskH = 120; var startX = 400; var startY = 700; var gapX = 320; var gapY = 400; for (var row = 0; row < deskRows; row++) { for (var col = 0; col < deskCols; col++) { // Dejar pasillo central if (col === 1 && row === 1) continue; var desk = LK.getAsset('furniture', { width: deskW, height: deskH, anchorX: 0.5, anchorY: 0.5, x: startX + col * gapX, y: startY + row * gapY }); game.addChild(desk); game.level8Desks.push(desk); } } // Texto de instrucción var instrText8 = new Text2("¡Esquiva los escritorios y escapa por la puerta!", { size: 110, fill: 0x1E90FF }); instrText8.anchor.set(0.5, 0.5); instrText8.x = 2048 / 2; instrText8.y = 320; game.addChild(instrText8); } if (level !== 5 && level !== 6 && level !== 7) { game.addChild(door); } // Add student (player) and center camera on them student = new Student(); if (level === 1) { student.x = 300; student.y = 2732 / 2; } else if (level === 2) { // Start at the far left of the hallway student.x = 200; student.y = 2732 / 2; } else if (level === 3) { // Start at bottom left of the park student.x = 200; student.y = 2400; } else if (level === 4) { // Start at bottom left of the maze student.x = 120; student.y = 2600; } else if (level === 5) { // Start at bottom center student.x = 2048 / 2; student.y = 2500; } else if (level === 6) { // Start at bottom center student.x = 2048 / 2; student.y = 2500; } else if (level === 7) { // Start at bottom center for level 7 student.x = 2048 / 2; student.y = 2500; } else if (level === 8) { // Start at bottom left for level 8 student.x = 200; student.y = 2500; } game.addChild(student); // Add profesor (monster) as an AI-driven monster profesor = new ProfesorMonster(); if (level === 1) { profesor.x = 1600; profesor.y = 2732 / 2; } else if (level === 2) { // Profesor starts behind the student, at the left edge profesor.x = 60; profesor.y = 2732 / 2; // Make profesor slower for this level profesor.level2Slow = true; } else if (level === 3) { // Profesor starts at the center of the park profesor.x = 1024; profesor.y = 900; } else if (level === 4) { // Profesor starts at the bottom of the maze, far from the student profesor.x = 1800; profesor.y = 2600; } else if (level === 5) { // Profesor starts at the bottom left in level 5 and will chase the student profesor.x = 300; profesor.y = 2500; } else if (level === 6) { // Profesor starts at the bottom right in level 6 and will chase the student profesor.x = 1800; profesor.y = 2500; } else if (level === 7) { // No profesor in level 7 profesor = null; } else if (level === 8) { // Profesor en la esquina superior izquierda, velocidad lenta profesor.x = 200; profesor.y = 200; profesor.level8Slow = true; } if (level !== 7) { game.addChild(profesor); } // Camera offset for 3rd person (centered on student) cameraOffsetX = 1024; // half width cameraOffsetY = 1366; // half height // Reset popup/question state showMathQuestion = false; mathQuestionPopup = null; } var door, student, profesor, cameraOffsetX, cameraOffsetY; setupLevel(currentLevel); // Camera follow logic game.update = function () { // If start screen is active, pause game logic if (!gameStarted) return; // Update student if (student.update) student.update(); // Level 2: Check collision with obstacles (open doors) if (currentLevel === 2 && game.level2Obstacles && student) { for (var i = 0; i < game.level2Obstacles.length; i++) { var obs = game.level2Obstacles[i]; if (student.intersects(obs)) { // Block movement: revert to last position and prevent further movement in this update student.x = student.lastX; student.y = student.lastY; // Also update targetX/targetY so the student doesn't keep trying to move into the obstacle student.targetX = student.lastX; student.targetY = student.lastY; break; // Stop checking further obstacles this frame } } // Corridor wall collision (block student from passing through corridor) // Get corridor bounds from setupLevel var corridorHeight = 520; var corridorY = (2732 - corridorHeight) / 2; var corridorTop = corridorY; var corridorBottom = corridorY + corridorHeight; // Student's bounding box var studentTop = student.y - 60; var studentBottom = student.y + 60; // If student tries to go above the corridor if (studentTop < corridorTop) { student.y = corridorTop + 60; } // If student tries to go below the corridor if (studentBottom > corridorBottom) { student.y = corridorBottom - 60; } // Block left and right walls var leftWall = 40; var rightWall = 2048 - 40; var studentLeft = student.x - 60; var studentRight = student.x + 60; if (studentLeft < leftWall) { student.x = leftWall + 60; } if (studentRight > rightWall) { student.x = rightWall - 60; } } // Level 3: Check collision with rocky obstacles if (currentLevel === 3 && game.level3Obstacles && student) { for (var i = 0; i < game.level3Obstacles.length; i++) { var rock = game.level3Obstacles[i]; if (student.intersects(rock)) { // Block movement: revert to last position and prevent further movement in this update student.x = student.lastX; student.y = student.lastY; student.targetX = student.lastX; student.targetY = student.lastY; break; } } // Block map boundaries var leftWall = 40; var rightWall = 2048 - 40; var topWall = 40; var bottomWall = 2732 - 40; var studentLeft = student.x - 60; var studentRight = student.x + 60; var studentTop = student.y - 60; var studentBottom = student.y + 60; if (studentLeft < leftWall) { student.x = leftWall + 60; } if (studentRight > rightWall) { student.x = rightWall - 60; } if (studentTop < topWall) { student.y = topWall + 60; } if (studentBottom > bottomWall) { student.y = bottomWall - 60; } } // Level 4: Check collision with maze walls and soccer balls if (currentLevel === 4 && student) { // Maze wall collision if (game.level4MazeWalls) { for (var i = 0; i < game.level4MazeWalls.length; i++) { var wall = game.level4MazeWalls[i]; if (student.intersects(wall)) { student.x = student.lastX; student.y = student.lastY; student.targetX = student.lastX; student.targetY = student.lastY; break; } } } // Soccer ball collision removed for level 4 // Block map boundaries var leftWall = 40; var rightWall = 2048 - 40; var topWall = 40; var bottomWall = 2732 - 40; var studentLeft = student.x - 60; var studentRight = student.x + 60; var studentTop = student.y - 60; var studentBottom = student.y + 60; if (studentLeft < leftWall) { student.x = leftWall + 60; } if (studentRight > rightWall) { student.x = rightWall - 60; } if (studentTop < topWall) { student.y = topWall + 60; } if (studentBottom > bottomWall) { student.y = bottomWall - 60; } } // Update profesor monster AI if (profesor && profesor.update) { // Slow profesor in level 2 if (currentLevel === 2 && profesor.level2Slow) { // Temporarily override update to move slower profesor.update = function () { this.lastX = this.x; this.lastY = this.y; if (typeof showMathQuestion !== "undefined" && showMathQuestion || typeof student !== "undefined" && typeof student.lastWasIntersectingDoor !== "undefined" && student.lastWasIntersectingDoor) { return; } if (typeof student === "undefined" || !student) return; var dx = student.x - this.x; var dy = student.y - this.y; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 3; // slower speed for level 2 if (dist > speed) { this.x += dx / dist * speed; this.y += dy / dist * speed; } else { this.x = student.x; this.y = student.y; } }; } // Slow profesor in level 8 if (currentLevel === 8 && profesor.level8Slow) { profesor.update = function () { this.lastX = this.x; this.lastY = this.y; if (typeof showMathQuestion !== "undefined" && showMathQuestion) { return; } if (typeof student === "undefined" || !student) return; var dx = student.x - this.x; var dy = student.y - this.y; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 2.5; // even slower for level 8 if (dist > speed) { this.x += dx / dist * speed; this.y += dy / dist * speed; } else { this.x = student.x; this.y = student.y; } }; } profesor.update(); } // Check for kill: profesor touches student (collision) var isIntersecting = profesor.intersects(student); if (!profesor.lastWasIntersecting && isIntersecting) { // Profesor kills student: show game over LK.effects.flashScreen(0xff0000, 1000); // Reset to level 1 after game over currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1200); LK.showGameOver(); } profesor.lastWasIntersecting = isIntersecting; // Level 6: Obstáculos móviles y dos puertas if (currentLevel === 6 && game.level6MovingObstacles && student) { // Obstáculos móviles: mover y checar colisión for (var i = 0; i < game.level6MovingObstacles.length; i++) { var obs = game.level6MovingObstacles[i]; // Guardar última posición if (typeof obs.lastX === "undefined") obs.lastX = obs.x; // Movimiento horizontal obs.x += obs._moveDir * obs._moveSpeed; if (obs.x < obs._moveMin) { obs.x = obs._moveMin; obs._moveDir = 1; } if (obs.x > obs._moveMax) { obs.x = obs._moveMax; obs._moveDir = -1; } // Colisión con el estudiante if (student.intersects(obs)) { // Game over LK.effects.flashScreen(0xff0000, 1000); currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1200); LK.showGameOver(); return; } obs.lastX = obs.x; } // Checar si toca una puerta if (game.level6Doors) { for (var i = 0; i < game.level6Doors.length; i++) { var d = game.level6Doors[i]; if (!d._alreadyTriggered && student.intersects(d)) { d._alreadyTriggered = true; // Mostrar popup de resultado var popup = new Container(); var bg = LK.getAsset('furniture', { width: 2048, height: 2732, color: 0x000000, anchorX: 0, anchorY: 0 }); bg.alpha = 0.01; popup.addChild(bg); var msg = d.isCorrectDoor ? "¡Escapaste del patio!" : "¡Esa no es la puerta de salida!"; var color = d.isCorrectDoor ? 0x00FF00 : 0xFF4444; var text = new Text2(msg, { size: 180, fill: color }); text.anchor.set(0.5, 0.5); text.x = 2048 / 2; text.y = 900; popup.addChild(text); game.addChild(popup); if (d.isCorrectDoor) { LK.effects.flashScreen(0x00ff00, 800); LK.setTimeout(function () { if (popup && popup.parent) popup.parent.removeChild(popup); LK.showYouWin(); }, 1200); } else { LK.effects.flashScreen(0xff0000, 800); LK.setTimeout(function () { if (popup && popup.parent) popup.parent.removeChild(popup); currentLevel = 1; setupLevel(currentLevel); LK.showGameOver(); }, 1200); } break; } } } // No más lógica de puertas para este frame return; } // Level 7: Laser room logic if (currentLevel === 7 && game.level7Lasers && student) { // Move lasers and check collision for (var i = 0; i < game.level7Lasers.length; i++) { var laser = game.level7Lasers[i]; if (laser._vertical) { laser.y += laser._moveDir * laser._moveSpeed; if (laser.y < laser._moveMin) { laser.y = laser._moveMin; laser._moveDir = 1; } if (laser.y > laser._moveMax) { laser.y = laser._moveMax; laser._moveDir = -1; } } else { laser.x += laser._moveDir * laser._moveSpeed; if (laser.x < laser._moveMin) { laser.x = laser._moveMin; laser._moveDir = 1; } if (laser.x > laser._moveMax) { laser.x = laser._moveMax; laser._moveDir = -1; } } // Check collision with student if (student.intersects(laser)) { LK.effects.flashScreen(0xff0000, 1000); currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1200); LK.showGameOver(); return; } } // Check if student touches the exit door if (game.level7Door && !game.level7Door._alreadyTriggered && student.intersects(game.level7Door)) { game.level7Door._alreadyTriggered = true; var popup = new Container(); var bg = LK.getAsset('furniture', { width: 2048, height: 2732, color: 0x000000, anchorX: 0, anchorY: 0 }); bg.alpha = 0.01; popup.addChild(bg); var text = new Text2("¡Escapaste del nivel láser!", { size: 180, fill: 0x00FF00 }); text.anchor.set(0.5, 0.5); text.x = 2048 / 2; text.y = 900; popup.addChild(text); game.addChild(popup); LK.effects.flashScreen(0x00ff00, 800); LK.setTimeout(function () { if (popup && popup.parent) popup.parent.removeChild(popup); LK.showYouWin(); }, 1200); return; } // No more logic for this frame return; } // Level 8: Sala de cómputo lógica if (currentLevel === 8 && game.level8Desks && student) { // Colisión con escritorios for (var i = 0; i < game.level8Desks.length; i++) { var desk = game.level8Desks[i]; if (student.intersects(desk)) { student.x = student.lastX; student.y = student.lastY; student.targetX = student.lastX; student.targetY = student.lastY; break; } } // Colisión con paredes (igual que otros niveles) var leftWall = 40; var rightWall = 2048 - 40; var topWall = 40; var bottomWall = 2732 - 40; var studentLeft = student.x - 60; var studentRight = student.x + 60; var studentTop = student.y - 60; var studentBottom = student.y + 60; if (studentLeft < leftWall) student.x = leftWall + 60; if (studentRight > rightWall) student.x = rightWall - 60; if (studentTop < topWall) student.y = topWall + 60; if (studentBottom > bottomWall) student.y = bottomWall - 60; // Colisión con profesor if (profesor && student.intersects(profesor)) { LK.effects.flashScreen(0xff0000, 1000); currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1200); LK.showGameOver(); return; } // Checar si toca la puerta de salida if (game.level8Door && !game.level8Door._alreadyTriggered && student.intersects(game.level8Door)) { game.level8Door._alreadyTriggered = true; var popup = new Container(); var bg = LK.getAsset('furniture', { width: 2048, height: 2732, color: 0x000000, anchorX: 0, anchorY: 0 }); bg.alpha = 0.01; popup.addChild(bg); var text = new Text2("¡Escapaste de la sala de cómputo!", { size: 180, fill: 0x00FF00 }); text.anchor.set(0.5, 0.5); text.x = 2048 / 2; text.y = 900; popup.addChild(text); game.addChild(popup); LK.effects.flashScreen(0x00ff00, 800); LK.setTimeout(function () { if (popup && popup.parent) popup.parent.removeChild(popup); LK.showYouWin(); }, 1200); return; } // No más lógica para este frame return; } // Check if student touches the door (minimum contact triggers question) if (currentLevel === 5 && game.level5Doors && student) { // Only trigger if not already showing a popup if (typeof showMathQuestion === "undefined" || !showMathQuestion) { for (var i = 0; i < game.level5Doors.length; i++) { var d = game.level5Doors[i]; if (!d._alreadyTriggered && student.intersects(d)) { d._alreadyTriggered = true; showMathQuestion = true; // Show popup: ¿Es esta la puerta correcta? var popup = new Container(); var bg = LK.getAsset('furniture', { width: 2048, height: 2732, color: 0x000000, anchorX: 0, anchorY: 0 }); bg.alpha = 0.01; popup.addChild(bg); var msg = d.isCorrectDoor ? "¡Felicidades! Escapaste del colegio." : "¡Esa no es la puerta correcta!"; var color = d.isCorrectDoor ? 0x00FF00 : 0xFF4444; var text = new Text2(msg, { size: 180, fill: color }); text.anchor.set(0.5, 0.5); text.x = 2048 / 2; text.y = 900; popup.addChild(text); game.addChild(popup); if (d.isCorrectDoor) { LK.effects.flashScreen(0x00ff00, 800); LK.setTimeout(function () { if (popup && popup.parent) popup.parent.removeChild(popup); showMathQuestion = false; // Advance to level 6 instead of showing win screen currentLevel = 6; setupLevel(currentLevel); }, 1200); } else { LK.effects.flashScreen(0xff0000, 800); LK.setTimeout(function () { if (popup && popup.parent) popup.parent.removeChild(popup); showMathQuestion = false; currentLevel = 1; setupLevel(currentLevel); LK.showGameOver(); }, 1200); } break; } } } } else // Check if student touches the door (minimum contact triggers question) if ((typeof showMathQuestion === "undefined" || !showMathQuestion) && door && student && !student.lastWasIntersectingDoor && student.intersects(door)) { showMathQuestion = true; student.lastWasIntersectingDoor = true; // Show the math question in-game as a fullscreen overlay with large, high-contrast text and options if (typeof mathQuestionPopup === "undefined" || !mathQuestionPopup) { mathQuestionPopup = new Container(); // Fullscreen transparent background (no purple, just to block input) var bg = LK.getAsset('furniture', { width: 2048, height: 2732, color: 0x000000, anchorX: 0, anchorY: 0 }); bg.alpha = 0.01; // almost invisible, just to block input mathQuestionPopup.addChild(bg); // Define questions and answers for each level/door // You can expand this array for more levels/doors var questions = [{ question: "¿Cuánto es 8 × 6?", options: [{ text: "1) 42", correct: false }, { text: "2) 48", correct: true }, { text: "3) 54", correct: false }] }, { question: "¿Cuánto es 7 × 22?", options: [{ text: "1) 144", correct: false }, { text: "2) 154", correct: true }, { text: "3) 127", correct: false }] }, { question: "¿Cuánto es 13 × 5?", options: [{ text: "1) 65", correct: true }, { text: "2) 45", correct: false }, { text: "3) 75", correct: false }] }]; // Pick question by level (default to first if out of range) var qIndex = typeof currentLevel !== "undefined" && currentLevel - 1 < questions.length ? currentLevel - 1 : 0; var qData = questions[qIndex]; // Question text - very large, high-contrast, centered var questionText = new Text2(qData.question, { size: 180, fill: 0xFFF700 // bright yellow for contrast }); questionText.anchor.set(0.5, 0); questionText.x = 2048 / 2; questionText.y = 320; mathQuestionPopup.addChild(questionText); // Render options var optionYStart = 800; var optionYStep = 300; var optionObjs = []; for (var i = 0; i < qData.options.length; i++) { var opt = qData.options[i]; var optObj = new Text2(opt.text, { size: 140, fill: 0xFFFFFF }); optObj.anchor.set(0.5, 0); optObj.x = 2048 / 2; optObj.y = optionYStart + i * optionYStep; mathQuestionPopup.addChild(optObj); optionObjs.push(optObj); // Add interaction for options optObj.interactive = true; optObj.buttonMode = true; if (opt.correct) { optObj.down = function () { if (typeof mathQuestionTimeout !== "undefined") { LK.clearTimeout(mathQuestionTimeout); mathQuestionTimeout = undefined; } LK.effects.flashScreen(0x00ff00, 800); // Open the door: remove it from the game scene if (door && door.parent) { door.parent.removeChild(door); door = null; } if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup); mathQuestionPopup = null; showMathQuestion = false; // Advance to next level or win if (typeof currentLevel !== "undefined" && typeof maxLevel !== "undefined") { if (currentLevel === 5) { // If on level 5, advance to level 6 instead of showing win currentLevel = 6; setupLevel(currentLevel); } else if (currentLevel < maxLevel) { currentLevel += 1; setupLevel(currentLevel); } else { LK.showYouWin(); } } }; } else { optObj.down = function () { if (typeof mathQuestionTimeout !== "undefined") { LK.clearTimeout(mathQuestionTimeout); mathQuestionTimeout = undefined; } LK.effects.flashScreen(0xff0000, 800); // Reset to level 1 after game over currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1000); LK.showGameOver(); if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup); mathQuestionPopup = null; showMathQuestion = false; }; } } // Add timer text var timeLimit = 10; // seconds var timeLeft = timeLimit; var timerText = new Text2("Tiempo: " + timeLeft, { size: 120, fill: 0xFF4444 }); timerText.anchor.set(0.5, 0); timerText.x = 2048 / 2; timerText.y = 650; mathQuestionPopup.addChild(timerText); // Timer logic if (typeof mathQuestionTimeout !== "undefined") { LK.clearTimeout(mathQuestionTimeout); mathQuestionTimeout = undefined; } if (typeof mathQuestionInterval !== "undefined") { LK.clearInterval(mathQuestionInterval); mathQuestionInterval = undefined; } mathQuestionInterval = LK.setInterval(function () { if (typeof timeLeft !== "undefined") { timeLeft -= 1; if (timerText && timerText.setText) timerText.setText("Tiempo: " + timeLeft); if (timeLeft <= 0) { // Time's up! Fail the question if (typeof mathQuestionTimeout !== "undefined") { LK.clearTimeout(mathQuestionTimeout); mathQuestionTimeout = undefined; } if (typeof mathQuestionInterval !== "undefined") { LK.clearInterval(mathQuestionInterval); mathQuestionInterval = undefined; } LK.effects.flashScreen(0xff0000, 800); currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1000); LK.showGameOver(); if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup); mathQuestionPopup = null; showMathQuestion = false; } } }, 1000); // Also set a hard timeout in case interval fails mathQuestionTimeout = LK.setTimeout(function () { if (typeof mathQuestionInterval !== "undefined") { LK.clearInterval(mathQuestionInterval); mathQuestionInterval = undefined; } LK.effects.flashScreen(0xff0000, 800); currentLevel = 1; LK.setTimeout(function () { setupLevel(currentLevel); }, 1000); LK.showGameOver(); if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup); mathQuestionPopup = null; showMathQuestion = false; }, timeLimit * 1000); // Fullscreen, no pivot, top-left at (0,0) mathQuestionPopup.x = 0; mathQuestionPopup.y = 0; game.addChild(mathQuestionPopup); } } // Track lastX for student for event logic student.lastX = student.x; student.lastY = student.y; // Track last intersection with door for event logic if (door && student) { if (typeof student.lastWasIntersectingDoor === "undefined") student.lastWasIntersectingDoor = false; var nowIntersectingDoor = student.intersects(door); student.lastWasIntersectingDoor = nowIntersectingDoor; } // Camera follows student from behind (3rd person, offset behind student direction) var dx = student.targetX - student.x; var dy = student.targetY - student.y; var dist = Math.sqrt(dx * dx + dy * dy); var camBehindDist = 350; // Distance behind the student var camX, camY; if (dist > 10) { // Camera is offset behind the student, in the opposite direction of movement camX = student.x - dx / dist * camBehindDist; camY = student.y - dy / dist * camBehindDist; } else { // If not moving, camera stays slightly above/behind camX = student.x - camBehindDist; camY = student.y; } // Clamp camera to map bounds camX = Math.max(0, Math.min(camX, 2048 - 2048)); camY = Math.max(0, Math.min(camY, 2732 - 2732)); game.x = -camX; game.y = -camY; }; // On-screen joystick controls for student movement // Joystick visual base var joystickBase = LK.getAsset('furniture', { width: 260, height: 260, color: 0x222222, anchorX: 0.5, anchorY: 0.5, x: 260, y: 2732 - 260 }); joystickBase.alpha = 0.25; LK.gui.left.addChild(joystickBase); // Joystick thumb var joystickThumb = LK.getAsset('furniture', { width: 120, height: 120, color: 0xffffff, anchorX: 0.5, anchorY: 0.5, x: 260, y: 2732 - 260 }); joystickThumb.alpha = 0.7; LK.gui.left.addChild(joystickThumb); var joystickActive = false; var joystickStartX = 0; var joystickStartY = 0; var joystickRadius = 110; // Helper to convert GUI to game coordinates function guiToGameCoords(guiX, guiY) { // For this game, joystick is always at bottom left, so just return as is return { x: guiX, y: guiY }; } // Joystick down joystickBase.interactive = true; joystickBase.buttonMode = true; joystickBase.down = function (x, y, obj) { joystickActive = true; joystickStartX = joystickBase.x; joystickStartY = joystickBase.y; joystickThumb.x = x; joystickThumb.y = y; }; // Joystick move joystickBase.move = function (x, y, obj) { if (!joystickActive) return; var dx = x - joystickStartX; var dy = y - joystickStartY; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > joystickRadius) { dx = dx / dist * joystickRadius; dy = dy / dist * joystickRadius; } joystickThumb.x = joystickStartX + dx; joystickThumb.y = joystickStartY + dy; // Move student target relative to joystick direction var moveStrength = dist > 20 ? 1 : 0; // Deadzone if (moveStrength) { // Map joystick to game area var moveX = student.x + dx * 3; var moveY = student.y + dy * 3; student.targetX = Math.max(60, Math.min(2048 - 60, moveX)); student.targetY = Math.max(60, Math.min(2732 - 60, moveY)); } }; // Joystick up joystickBase.up = function (x, y, obj) { joystickActive = false; joystickThumb.x = joystickBase.x; joystickThumb.y = joystickBase.y; }; // Also allow drag-to-move for desktop or direct touch game.down = function (x, y, obj) { if (!gameStarted) return; if (!joystickActive) { student.targetX = x; student.targetY = y; } }; game.move = function (x, y, obj) { if (!gameStarted) return; if (!joystickActive) { student.targetX = x; student.targetY = y; } }; game.up = function (x, y, obj) { // No-op for now };
/****
* Classes
****/
// ProfesorMonster class: AI-driven monster that chases the student
var ProfesorMonster = Container.expand(function () {
var self = Container.call(this);
// Attach profesor asset
var profesorSprite = self.attachAsset('profesor', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for event logic
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
// Update method: chase the student
self.update = function () {
// Store last position
self.lastX = self.x;
self.lastY = self.y;
// Immobilize profesor if student is touching the door (popup active)
if (typeof showMathQuestion !== "undefined" && showMathQuestion || typeof student !== "undefined" && typeof student.lastWasIntersectingDoor !== "undefined" && student.lastWasIntersectingDoor) {
// Profesor stays still, do not move
return;
}
// Use global student variable
if (typeof student === "undefined" || !student) return;
// Simple AI: move towards the student
var dx = student.x - self.x;
var dy = student.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 6; // profesor speed, now much slower than student
if (dist > speed) {
// Calculate intended new position
var nextX = self.x + dx / dist * speed;
var nextY = self.y + dy / dist * speed;
var blocked = false;
// Level 2: block profesor on obstacles
if (typeof currentLevel !== "undefined" && currentLevel === 2 && typeof game !== "undefined" && game.level2Obstacles) {
for (var i = 0; i < game.level2Obstacles.length; i++) {
var obs = game.level2Obstacles[i];
// Simulate move: temporarily set position, check collision, revert
var oldX = self.x,
oldY = self.y;
self.x = nextX;
self.y = nextY;
if (self.intersects(obs)) {
blocked = true;
}
self.x = oldX;
self.y = oldY;
if (blocked) break;
}
}
// Level 2: also block corridor walls
if (!blocked && typeof currentLevel !== "undefined" && currentLevel === 2) {
var corridorHeight = 520;
var corridorY = (2732 - corridorHeight) / 2;
var corridorTop = corridorY;
var corridorBottom = corridorY + corridorHeight;
var leftWall = 40;
var rightWall = 2048 - 40;
var profesorTop = nextY - 80;
var profesorBottom = nextY + 80;
var profesorLeft = nextX - 80;
var profesorRight = nextX + 80;
if (profesorTop < corridorTop || profesorBottom > corridorBottom || profesorLeft < leftWall || profesorRight > rightWall) {
blocked = true;
}
}
// Level 3: block profesor on rocks and map boundaries
if (!blocked && typeof currentLevel !== "undefined" && currentLevel === 3 && typeof game !== "undefined" && game.level3Obstacles) {
for (var i = 0; i < game.level3Obstacles.length; i++) {
var rock = game.level3Obstacles[i];
var oldX = self.x,
oldY = self.y;
self.x = nextX;
self.y = nextY;
if (self.intersects(rock)) {
blocked = true;
}
self.x = oldX;
self.y = oldY;
if (blocked) break;
}
// Map boundaries for profesor in level 3
var leftWall = 40;
var rightWall = 2048 - 40;
var topWall = 40;
var bottomWall = 2732 - 40;
var profesorLeft = nextX - 80;
var profesorRight = nextX + 80;
var profesorTop = nextY - 80;
var profesorBottom = nextY + 80;
if (profesorLeft < leftWall || profesorRight > rightWall || profesorTop < topWall || profesorBottom > bottomWall) {
blocked = true;
}
}
// Level 4: block profesor on maze walls and soccer balls and map boundaries
if (!blocked && typeof currentLevel !== "undefined" && currentLevel === 4 && typeof game !== "undefined") {
// Maze walls
if (game.level4MazeWalls) {
for (var i = 0; i < game.level4MazeWalls.length; i++) {
var wall = game.level4MazeWalls[i];
var oldX = self.x,
oldY = self.y;
self.x = nextX;
self.y = nextY;
if (self.intersects(wall)) {
blocked = true;
}
self.x = oldX;
self.y = oldY;
if (blocked) break;
}
}
// Soccer balls collision removed for level 4
// Map boundaries for profesor in level 4
var leftWall = 40;
var rightWall = 2048 - 40;
var topWall = 40;
var bottomWall = 2732 - 40;
var profesorLeft = nextX - 80;
var profesorRight = nextX + 80;
var profesorTop = nextY - 80;
var profesorBottom = nextY + 80;
if (profesorLeft < leftWall || profesorRight > rightWall || profesorTop < topWall || profesorBottom > bottomWall) {
blocked = true;
}
}
// If not blocked, move
if (!blocked) {
self.x = nextX;
self.y = nextY;
}
} else {
self.x = student.x;
self.y = student.y;
}
};
return self;
});
// Student class: controllable player character
var Student = Container.expand(function () {
var self = Container.call(this);
// Attach student asset
var studentSprite = self.attachAsset('student', {
anchorX: 0.5,
anchorY: 0.5
});
// Initial target position is current position
self.targetX = 300;
self.targetY = 2732 / 2;
// Track last position for event logic
self.lastX = self.x;
self.lastY = self.y;
// Update method: move towards target position
self.update = function () {
// Store last position
self.lastX = self.x;
self.lastY = self.y;
// Move towards targetX, targetY
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 18; // pixels per frame
if (dist > speed) {
self.x += dx / dist * speed;
self.y += dy / dist * speed;
} else {
self.x = self.targetX;
self.y = self.targetY;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Level system
// Rocky park floor for level 3 (must be present in asset library)
var currentLevel = 1;
var maxLevel = 8; // Now 8 levels (added sala de computo)
// --- START SCREEN LOGIC ---
var startScreenOverlay = null;
var gameStarted = false;
function showStartScreen() {
if (startScreenOverlay) return;
startScreenOverlay = new Container();
// Only a single prominent JUGAR button, centered
var playBtn = LK.getAsset('furniture', {
width: 600,
height: 220,
color: 0x83de44,
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1200
});
playBtn.interactive = true;
playBtn.buttonMode = true;
startScreenOverlay.addChild(playBtn);
var playText = new Text2("JUGAR", {
size: 150,
fill: 0x000000
});
playText.anchor.set(0.5, 0.5);
playText.x = playBtn.x;
playText.y = playBtn.y;
startScreenOverlay.addChild(playText);
playBtn.down = function () {
if (startScreenOverlay && startScreenOverlay.parent) {
startScreenOverlay.parent.removeChild(startScreenOverlay);
}
startScreenOverlay = null;
gameStarted = true;
setupLevel(currentLevel);
};
// Add overlay to game (not GUI, so it blocks input)
game.addChild(startScreenOverlay);
}
// Show start screen on load
LK.setTimeout(function () {
showStartScreen();
}, 10);
// Helper to clear all children except joystick and overlays
function clearGameObjects() {
// Remove all children except overlays and joystick
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
// Don't remove overlays or joystick (which are in LK.gui)
game.removeChild(child);
}
// Clear level 2 obstacles array if present
if (game.level2Obstacles) {
game.level2Obstacles.length = 0;
}
}
// Helper to setup a level
function setupLevel(level) {
clearGameObjects();
// Main floor (covers the whole map) - different for each level
var floorAssetId;
if (level === 1) {
floorAssetId = 'floor'; // Default school floor
} else if (level === 2) {
floorAssetId = 'floor'; // Piso normal para el nivel 2
} else if (level === 3) {
floorAssetId = 'floor_rocky'; // Use a rocky park floor for level 3
} else if (level === 4) {
floorAssetId = 'floor_rocky'; // Piso de concreto para el nivel 4
}
// Only add a floor if floorAssetId is defined (skip for level 5)
if (typeof floorAssetId !== "undefined") {
var floor = LK.getAsset(floorAssetId, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(floor);
}
// Top and bottom walls (main corridor)
var wallTop, wallBottom, wallLeft, wallRight;
if (level === 3) {
// Level 3: Rocky park, open area with rocks as obstacles and a new door position
// Remove corridor walls, use only map boundaries
wallTop = LK.getAsset('wall', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
wallBottom = LK.getAsset('wall', {
x: 0,
y: 2732 - 40,
anchorX: 0,
anchorY: 0
});
wallLeft = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
wallRight = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 2048 - 40,
y: 0,
anchorX: 0,
anchorY: 0
});
} else if (level === 2) {
// Narrow hallway: corridor is 520px tall, centered vertically
var corridorHeight = 520;
var corridorY = (2732 - corridorHeight) / 2;
wallTop = LK.getAsset('wall', {
x: 0,
y: corridorY - 40,
anchorX: 0,
anchorY: 0
});
wallBottom = LK.getAsset('wall', {
x: 0,
y: corridorY + corridorHeight,
anchorX: 0,
anchorY: 0
});
// Left and right walls (vertical boundaries)
wallLeft = LK.getAsset('wall', {
width: 40,
height: corridorHeight,
x: 0,
y: corridorY,
anchorX: 0,
anchorY: 0
});
wallRight = LK.getAsset('wall', {
width: 40,
height: corridorHeight,
x: 2048 - 40,
y: corridorY,
anchorX: 0,
anchorY: 0
});
} else {
wallTop = LK.getAsset('wall', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
wallBottom = LK.getAsset('wall', {
x: 0,
y: 2732 - 40,
anchorX: 0,
anchorY: 0
});
wallLeft = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
wallRight = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 2048 - 40,
y: 0,
anchorX: 0,
anchorY: 0
});
}
game.addChild(wallTop);
game.addChild(wallBottom);
game.addChild(wallLeft);
game.addChild(wallRight);
// Add door (exit) for each level
if (level === 1) {
door = LK.getAsset('door', {
x: 2048 - 200,
y: 2200,
anchorX: 0,
anchorY: 0
});
} else if (level === 2) {
// Level 2: Hallway with open doors as obstacles, exit at the end, profesor behind
// Place the exit door at the far right end of the hallway, centered in corridor
var corridorHeight = 520;
var corridorY = (2732 - corridorHeight) / 2;
door = LK.getAsset('door', {
x: 2048 - 200,
y: corridorY + corridorHeight / 2 - 200,
anchorX: 0,
anchorY: 0
});
// Add open doors as obstacles along the hallway
// We'll use furniture assets as open doors (obstacles)
var numObstacles = 3;
var spacing = (2048 - 400) / (numObstacles + 1);
for (var i = 1; i <= numObstacles; i++) {
var obsX = i * spacing + 100;
// Obstacles alternate above and below the center line, but always inside the corridor
var obsY;
if (i % 2 === 0) {
obsY = corridorY + 120;
} else {
obsY = corridorY + corridorHeight - 120;
}
var obstacle = LK.getAsset('furniture', {
width: 180,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
x: obsX,
y: obsY
});
game.addChild(obstacle);
// Store obstacles for collision in global array
if (!game.level2Obstacles) game.level2Obstacles = [];
game.level2Obstacles.push(obstacle);
}
}
// Level 3: Add rocky obstacles and new door position
if (level === 3) {
// Place the exit door at the top right corner of the park
door = LK.getAsset('door', {
x: 2048 - 320,
y: 180,
anchorX: 0,
anchorY: 0
});
// Add rocky obstacles (use 'furniture' asset as rocks, but with different positions and sizes)
var rocks = [{
x: 600,
y: 700,
w: 180,
h: 120
}, {
x: 1200,
y: 500,
w: 220,
h: 100
}, {
x: 900,
y: 1400,
w: 160,
h: 160
}, {
x: 1500,
y: 900,
w: 200,
h: 120
}, {
x: 400,
y: 2000,
w: 180,
h: 120
}];
if (!game.level3Obstacles) game.level3Obstacles = [];
game.level3Obstacles.length = 0;
for (var i = 0; i < rocks.length; i++) {
var r = rocks[i];
var rock = LK.getAsset('furniture', {
width: r.w,
height: r.h,
anchorX: 0.5,
anchorY: 0.5,
x: r.x,
y: r.y
});
game.addChild(rock);
game.level3Obstacles.push(rock);
}
}
// Level 4: Add maze, soccer balls, and door
if (level === 4) {
// Place the exit door at the top right of the maze
door = LK.getAsset('door', {
x: 2048 - 320,
y: 180,
anchorX: 0,
anchorY: 0
});
// Maze walls (using 'wall' asset, horizontal and vertical)
// Each wall: {x, y, width, height}
// Easy maze: outer boundaries plus a few simple interior walls
var mazeWalls = [
// Outer boundaries
{
x: 0,
y: 0,
width: 2048,
height: 40
},
// top
{
x: 0,
y: 2732 - 40,
width: 2048,
height: 40
},
// bottom
{
x: 0,
y: 0,
width: 40,
height: 2732
},
// left
{
x: 2048 - 40,
y: 0,
width: 40,
height: 2732
},
// right
// A few easy interior walls (horizontal and vertical, leaving wide passages)
{
x: 400,
y: 600,
width: 1200,
height: 40
},
// horizontal, upper
{
x: 400,
y: 1500,
width: 1200,
height: 40
},
// horizontal, lower
{
x: 800,
y: 900,
width: 40,
height: 600
},
// vertical, left
{
x: 1200,
y: 1200,
width: 40,
height: 600
} // vertical, right
];
if (!game.level4MazeWalls) game.level4MazeWalls = [];
game.level4MazeWalls.length = 0;
for (var i = 0; i < mazeWalls.length; i++) {
var mw = mazeWalls[i];
var wall = LK.getAsset('wall', {
width: mw.width,
height: mw.height,
x: mw.x,
y: mw.y,
anchorX: 0,
anchorY: 0
});
game.addChild(wall);
game.level4MazeWalls.push(wall);
}
// Soccer balls as obstacles removed for level 4
if (!game.level4SoccerBalls) game.level4SoccerBalls = [];
game.level4SoccerBalls.length = 0;
} else if (level === 5) {
// Level 5: Adivina la puerta correcta (guess the correct door)
// Add floor for level 5
var floor = LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(floor);
// Three doors, only one is correct
if (!game.level5Doors) game.level5Doors = [];
game.level5Doors.length = 0;
// Place three doors spaced horizontally at the top of the map
var doorY = 300;
var doorSpacing = 500;
var firstDoorX = 524; // Centered: (2048 - (3*240 + 2*500))/2 = 524
var correctDoorIndex = Math.floor(Math.random() * 3); // Random correct door
for (var i = 0; i < 3; i++) {
var d = LK.getAsset('door', {
x: firstDoorX + i * (240 + doorSpacing),
y: doorY,
anchorX: 0,
anchorY: 0
});
d.isCorrectDoor = i === correctDoorIndex;
game.addChild(d);
game.level5Doors.push(d);
}
// Add centered text above the doors in level 5
var chooseText = new Text2("Escoje la puerta correcta", {
size: 120,
fill: 0xFFF700
});
chooseText.anchor.set(0.5, 0.5);
chooseText.x = 2048 / 2;
chooseText.y = doorY - 80;
game.addChild(chooseText);
// No maze walls, just outer boundaries
var wallTop = LK.getAsset('wall', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallBottom = LK.getAsset('wall', {
x: 0,
y: 2732 - 40,
anchorX: 0,
anchorY: 0
});
var wallLeft = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallRight = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 2048 - 40,
y: 0,
anchorX: 0,
anchorY: 0
});
game.addChild(wallTop);
game.addChild(wallBottom);
game.addChild(wallLeft);
game.addChild(wallRight);
// Add profesor for level 5 (will chase the student)
} else if (level === 6) {
// Nivel 6: Patio con UN obstáculo móvil y solo una puerta de salida
var floor = LK.getAsset('floor_placa', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(floor);
// Solo una puerta en la parte superior, es la salida
if (!game.level6Doors) game.level6Doors = [];
game.level6Doors.length = 0;
var doorY = 200;
var doorX = 1024 - 120; // Centrada horizontalmente (2048/2 - door width/2)
var d = LK.getAsset('door', {
x: doorX,
y: doorY,
anchorX: 0,
anchorY: 0
});
d.isCorrectDoor = true;
game.addChild(d);
game.level6Doors.push(d);
// Texto de pista
var chooseText6 = new Text2("¡Encuentra la puerta de salida!", {
size: 120,
fill: 0xFFF700
});
chooseText6.anchor.set(0.5, 0.5);
chooseText6.x = 2048 / 2;
chooseText6.y = doorY - 80;
game.addChild(chooseText6);
// Paredes exteriores
var wallTop = LK.getAsset('wall', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallBottom = LK.getAsset('wall', {
x: 0,
y: 2732 - 40,
anchorX: 0,
anchorY: 0
});
var wallLeft = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallRight = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 2048 - 40,
y: 0,
anchorX: 0,
anchorY: 0
});
game.addChild(wallTop);
game.addChild(wallBottom);
game.addChild(wallLeft);
game.addChild(wallRight);
// Obstáculo móvil (solo 1 bloque que se mueve horizontalmente)
if (!game.level6MovingObstacles) game.level6MovingObstacles = [];
game.level6MovingObstacles.length = 0;
var obsY = [1400]; // Solo un obstáculo, centrado verticalmente
for (var i = 0; i < 1; i++) {
var obs = LK.getAsset('furniture', {
width: 320,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: obsY[i]
});
obs._moveDir = 1;
obs._moveMin = 300;
obs._moveMax = 2048 - 300;
obs._moveSpeed = 10;
game.addChild(obs);
game.level6MovingObstacles.push(obs);
}
} else if (level === 7) {
// Nivel 7: Laser room - dodge moving laser beams and reach the exit door
var floor = LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(floor);
// Outer walls
var wallTop = LK.getAsset('wall', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallBottom = LK.getAsset('wall', {
x: 0,
y: 2732 - 40,
anchorX: 0,
anchorY: 0
});
var wallLeft = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallRight = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 2048 - 40,
y: 0,
anchorX: 0,
anchorY: 0
});
game.addChild(wallTop);
game.addChild(wallBottom);
game.addChild(wallLeft);
game.addChild(wallRight);
// Exit door at the top center
if (!game.level7Door) game.level7Door = null;
var doorY = 120;
var doorX = 2048 / 2 - 120;
var d7 = LK.getAsset('door', {
x: doorX,
y: doorY,
anchorX: 0,
anchorY: 0
});
d7.isCorrectDoor = true;
game.addChild(d7);
game.level7Door = d7;
// Laser beams (moving obstacles)
if (!game.level7Lasers) game.level7Lasers = [];
game.level7Lasers.length = 0;
// Each laser: {x, y, w, h, vertical, min, max, speed, dir}
var lasers = [
// Horizontal lasers
{
x: 300,
y: 700,
w: 1448,
h: 30,
vertical: false,
min: 700,
max: 1200,
speed: 8,
dir: 1
}, {
x: 300,
y: 1800,
w: 1448,
h: 30,
vertical: false,
min: 1400,
max: 2100,
speed: 10,
dir: -1
},
// Vertical lasers
{
x: 600,
y: 400,
w: 30,
h: 1000,
vertical: true,
min: 600,
max: 1400,
speed: 7,
dir: 1
}, {
x: 1400,
y: 1600,
w: 30,
h: 900,
vertical: true,
min: 900,
max: 2100,
speed: 9,
dir: -1
}];
for (var i = 0; i < lasers.length; i++) {
var l = lasers[i];
var laser = LK.getAsset('wall', {
width: l.w,
height: l.h,
x: l.x,
y: l.y,
anchorX: 0,
anchorY: 0
});
laser._isLaser = true;
laser._vertical = l.vertical;
laser._moveMin = l.min;
laser._moveMax = l.max;
laser._moveSpeed = l.speed;
laser._moveDir = l.dir;
game.addChild(laser);
game.level7Lasers.push(laser);
}
// Instruction text
var instrText = new Text2("¡Esquiva los rayos láser y escapa por la puerta!", {
size: 110,
fill: 0xFF2222
});
instrText.anchor.set(0.5, 0.5);
instrText.x = 2048 / 2;
instrText.y = 320;
game.addChild(instrText);
} else if (level === 8) {
// Nivel 8: Sala de cómputo con escritorios como obstáculos y una sola puerta de salida
var floor = LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(floor);
// Paredes exteriores
var wallTop = LK.getAsset('wall', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallBottom = LK.getAsset('wall', {
x: 0,
y: 2732 - 40,
anchorX: 0,
anchorY: 0
});
var wallLeft = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
});
var wallRight = LK.getAsset('wall', {
width: 40,
height: 2732,
x: 2048 - 40,
y: 0,
anchorX: 0,
anchorY: 0
});
game.addChild(wallTop);
game.addChild(wallBottom);
game.addChild(wallLeft);
game.addChild(wallRight);
// Puerta de salida en la parte superior derecha
if (!game.level8Door) game.level8Door = null;
var doorY = 120;
var doorX = 2048 - 320;
var d8 = LK.getAsset('door', {
x: doorX,
y: doorY,
anchorX: 0,
anchorY: 0
});
d8.isCorrectDoor = true;
game.addChild(d8);
game.level8Door = d8;
// Escritorios como obstáculos (usando 'furniture')
if (!game.level8Desks) game.level8Desks = [];
game.level8Desks.length = 0;
// Distribuir escritorios en filas y columnas, dejando pasillos
var deskRows = 3;
var deskCols = 4;
var deskW = 180;
var deskH = 120;
var startX = 400;
var startY = 700;
var gapX = 320;
var gapY = 400;
for (var row = 0; row < deskRows; row++) {
for (var col = 0; col < deskCols; col++) {
// Dejar pasillo central
if (col === 1 && row === 1) continue;
var desk = LK.getAsset('furniture', {
width: deskW,
height: deskH,
anchorX: 0.5,
anchorY: 0.5,
x: startX + col * gapX,
y: startY + row * gapY
});
game.addChild(desk);
game.level8Desks.push(desk);
}
}
// Texto de instrucción
var instrText8 = new Text2("¡Esquiva los escritorios y escapa por la puerta!", {
size: 110,
fill: 0x1E90FF
});
instrText8.anchor.set(0.5, 0.5);
instrText8.x = 2048 / 2;
instrText8.y = 320;
game.addChild(instrText8);
}
if (level !== 5 && level !== 6 && level !== 7) {
game.addChild(door);
}
// Add student (player) and center camera on them
student = new Student();
if (level === 1) {
student.x = 300;
student.y = 2732 / 2;
} else if (level === 2) {
// Start at the far left of the hallway
student.x = 200;
student.y = 2732 / 2;
} else if (level === 3) {
// Start at bottom left of the park
student.x = 200;
student.y = 2400;
} else if (level === 4) {
// Start at bottom left of the maze
student.x = 120;
student.y = 2600;
} else if (level === 5) {
// Start at bottom center
student.x = 2048 / 2;
student.y = 2500;
} else if (level === 6) {
// Start at bottom center
student.x = 2048 / 2;
student.y = 2500;
} else if (level === 7) {
// Start at bottom center for level 7
student.x = 2048 / 2;
student.y = 2500;
} else if (level === 8) {
// Start at bottom left for level 8
student.x = 200;
student.y = 2500;
}
game.addChild(student);
// Add profesor (monster) as an AI-driven monster
profesor = new ProfesorMonster();
if (level === 1) {
profesor.x = 1600;
profesor.y = 2732 / 2;
} else if (level === 2) {
// Profesor starts behind the student, at the left edge
profesor.x = 60;
profesor.y = 2732 / 2;
// Make profesor slower for this level
profesor.level2Slow = true;
} else if (level === 3) {
// Profesor starts at the center of the park
profesor.x = 1024;
profesor.y = 900;
} else if (level === 4) {
// Profesor starts at the bottom of the maze, far from the student
profesor.x = 1800;
profesor.y = 2600;
} else if (level === 5) {
// Profesor starts at the bottom left in level 5 and will chase the student
profesor.x = 300;
profesor.y = 2500;
} else if (level === 6) {
// Profesor starts at the bottom right in level 6 and will chase the student
profesor.x = 1800;
profesor.y = 2500;
} else if (level === 7) {
// No profesor in level 7
profesor = null;
} else if (level === 8) {
// Profesor en la esquina superior izquierda, velocidad lenta
profesor.x = 200;
profesor.y = 200;
profesor.level8Slow = true;
}
if (level !== 7) {
game.addChild(profesor);
}
// Camera offset for 3rd person (centered on student)
cameraOffsetX = 1024; // half width
cameraOffsetY = 1366; // half height
// Reset popup/question state
showMathQuestion = false;
mathQuestionPopup = null;
}
var door, student, profesor, cameraOffsetX, cameraOffsetY;
setupLevel(currentLevel);
// Camera follow logic
game.update = function () {
// If start screen is active, pause game logic
if (!gameStarted) return;
// Update student
if (student.update) student.update();
// Level 2: Check collision with obstacles (open doors)
if (currentLevel === 2 && game.level2Obstacles && student) {
for (var i = 0; i < game.level2Obstacles.length; i++) {
var obs = game.level2Obstacles[i];
if (student.intersects(obs)) {
// Block movement: revert to last position and prevent further movement in this update
student.x = student.lastX;
student.y = student.lastY;
// Also update targetX/targetY so the student doesn't keep trying to move into the obstacle
student.targetX = student.lastX;
student.targetY = student.lastY;
break; // Stop checking further obstacles this frame
}
}
// Corridor wall collision (block student from passing through corridor)
// Get corridor bounds from setupLevel
var corridorHeight = 520;
var corridorY = (2732 - corridorHeight) / 2;
var corridorTop = corridorY;
var corridorBottom = corridorY + corridorHeight;
// Student's bounding box
var studentTop = student.y - 60;
var studentBottom = student.y + 60;
// If student tries to go above the corridor
if (studentTop < corridorTop) {
student.y = corridorTop + 60;
}
// If student tries to go below the corridor
if (studentBottom > corridorBottom) {
student.y = corridorBottom - 60;
}
// Block left and right walls
var leftWall = 40;
var rightWall = 2048 - 40;
var studentLeft = student.x - 60;
var studentRight = student.x + 60;
if (studentLeft < leftWall) {
student.x = leftWall + 60;
}
if (studentRight > rightWall) {
student.x = rightWall - 60;
}
}
// Level 3: Check collision with rocky obstacles
if (currentLevel === 3 && game.level3Obstacles && student) {
for (var i = 0; i < game.level3Obstacles.length; i++) {
var rock = game.level3Obstacles[i];
if (student.intersects(rock)) {
// Block movement: revert to last position and prevent further movement in this update
student.x = student.lastX;
student.y = student.lastY;
student.targetX = student.lastX;
student.targetY = student.lastY;
break;
}
}
// Block map boundaries
var leftWall = 40;
var rightWall = 2048 - 40;
var topWall = 40;
var bottomWall = 2732 - 40;
var studentLeft = student.x - 60;
var studentRight = student.x + 60;
var studentTop = student.y - 60;
var studentBottom = student.y + 60;
if (studentLeft < leftWall) {
student.x = leftWall + 60;
}
if (studentRight > rightWall) {
student.x = rightWall - 60;
}
if (studentTop < topWall) {
student.y = topWall + 60;
}
if (studentBottom > bottomWall) {
student.y = bottomWall - 60;
}
}
// Level 4: Check collision with maze walls and soccer balls
if (currentLevel === 4 && student) {
// Maze wall collision
if (game.level4MazeWalls) {
for (var i = 0; i < game.level4MazeWalls.length; i++) {
var wall = game.level4MazeWalls[i];
if (student.intersects(wall)) {
student.x = student.lastX;
student.y = student.lastY;
student.targetX = student.lastX;
student.targetY = student.lastY;
break;
}
}
}
// Soccer ball collision removed for level 4
// Block map boundaries
var leftWall = 40;
var rightWall = 2048 - 40;
var topWall = 40;
var bottomWall = 2732 - 40;
var studentLeft = student.x - 60;
var studentRight = student.x + 60;
var studentTop = student.y - 60;
var studentBottom = student.y + 60;
if (studentLeft < leftWall) {
student.x = leftWall + 60;
}
if (studentRight > rightWall) {
student.x = rightWall - 60;
}
if (studentTop < topWall) {
student.y = topWall + 60;
}
if (studentBottom > bottomWall) {
student.y = bottomWall - 60;
}
}
// Update profesor monster AI
if (profesor && profesor.update) {
// Slow profesor in level 2
if (currentLevel === 2 && profesor.level2Slow) {
// Temporarily override update to move slower
profesor.update = function () {
this.lastX = this.x;
this.lastY = this.y;
if (typeof showMathQuestion !== "undefined" && showMathQuestion || typeof student !== "undefined" && typeof student.lastWasIntersectingDoor !== "undefined" && student.lastWasIntersectingDoor) {
return;
}
if (typeof student === "undefined" || !student) return;
var dx = student.x - this.x;
var dy = student.y - this.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 3; // slower speed for level 2
if (dist > speed) {
this.x += dx / dist * speed;
this.y += dy / dist * speed;
} else {
this.x = student.x;
this.y = student.y;
}
};
}
// Slow profesor in level 8
if (currentLevel === 8 && profesor.level8Slow) {
profesor.update = function () {
this.lastX = this.x;
this.lastY = this.y;
if (typeof showMathQuestion !== "undefined" && showMathQuestion) {
return;
}
if (typeof student === "undefined" || !student) return;
var dx = student.x - this.x;
var dy = student.y - this.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 2.5; // even slower for level 8
if (dist > speed) {
this.x += dx / dist * speed;
this.y += dy / dist * speed;
} else {
this.x = student.x;
this.y = student.y;
}
};
}
profesor.update();
}
// Check for kill: profesor touches student (collision)
var isIntersecting = profesor.intersects(student);
if (!profesor.lastWasIntersecting && isIntersecting) {
// Profesor kills student: show game over
LK.effects.flashScreen(0xff0000, 1000);
// Reset to level 1 after game over
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1200);
LK.showGameOver();
}
profesor.lastWasIntersecting = isIntersecting;
// Level 6: Obstáculos móviles y dos puertas
if (currentLevel === 6 && game.level6MovingObstacles && student) {
// Obstáculos móviles: mover y checar colisión
for (var i = 0; i < game.level6MovingObstacles.length; i++) {
var obs = game.level6MovingObstacles[i];
// Guardar última posición
if (typeof obs.lastX === "undefined") obs.lastX = obs.x;
// Movimiento horizontal
obs.x += obs._moveDir * obs._moveSpeed;
if (obs.x < obs._moveMin) {
obs.x = obs._moveMin;
obs._moveDir = 1;
}
if (obs.x > obs._moveMax) {
obs.x = obs._moveMax;
obs._moveDir = -1;
}
// Colisión con el estudiante
if (student.intersects(obs)) {
// Game over
LK.effects.flashScreen(0xff0000, 1000);
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1200);
LK.showGameOver();
return;
}
obs.lastX = obs.x;
}
// Checar si toca una puerta
if (game.level6Doors) {
for (var i = 0; i < game.level6Doors.length; i++) {
var d = game.level6Doors[i];
if (!d._alreadyTriggered && student.intersects(d)) {
d._alreadyTriggered = true;
// Mostrar popup de resultado
var popup = new Container();
var bg = LK.getAsset('furniture', {
width: 2048,
height: 2732,
color: 0x000000,
anchorX: 0,
anchorY: 0
});
bg.alpha = 0.01;
popup.addChild(bg);
var msg = d.isCorrectDoor ? "¡Escapaste del patio!" : "¡Esa no es la puerta de salida!";
var color = d.isCorrectDoor ? 0x00FF00 : 0xFF4444;
var text = new Text2(msg, {
size: 180,
fill: color
});
text.anchor.set(0.5, 0.5);
text.x = 2048 / 2;
text.y = 900;
popup.addChild(text);
game.addChild(popup);
if (d.isCorrectDoor) {
LK.effects.flashScreen(0x00ff00, 800);
LK.setTimeout(function () {
if (popup && popup.parent) popup.parent.removeChild(popup);
LK.showYouWin();
}, 1200);
} else {
LK.effects.flashScreen(0xff0000, 800);
LK.setTimeout(function () {
if (popup && popup.parent) popup.parent.removeChild(popup);
currentLevel = 1;
setupLevel(currentLevel);
LK.showGameOver();
}, 1200);
}
break;
}
}
}
// No más lógica de puertas para este frame
return;
}
// Level 7: Laser room logic
if (currentLevel === 7 && game.level7Lasers && student) {
// Move lasers and check collision
for (var i = 0; i < game.level7Lasers.length; i++) {
var laser = game.level7Lasers[i];
if (laser._vertical) {
laser.y += laser._moveDir * laser._moveSpeed;
if (laser.y < laser._moveMin) {
laser.y = laser._moveMin;
laser._moveDir = 1;
}
if (laser.y > laser._moveMax) {
laser.y = laser._moveMax;
laser._moveDir = -1;
}
} else {
laser.x += laser._moveDir * laser._moveSpeed;
if (laser.x < laser._moveMin) {
laser.x = laser._moveMin;
laser._moveDir = 1;
}
if (laser.x > laser._moveMax) {
laser.x = laser._moveMax;
laser._moveDir = -1;
}
}
// Check collision with student
if (student.intersects(laser)) {
LK.effects.flashScreen(0xff0000, 1000);
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1200);
LK.showGameOver();
return;
}
}
// Check if student touches the exit door
if (game.level7Door && !game.level7Door._alreadyTriggered && student.intersects(game.level7Door)) {
game.level7Door._alreadyTriggered = true;
var popup = new Container();
var bg = LK.getAsset('furniture', {
width: 2048,
height: 2732,
color: 0x000000,
anchorX: 0,
anchorY: 0
});
bg.alpha = 0.01;
popup.addChild(bg);
var text = new Text2("¡Escapaste del nivel láser!", {
size: 180,
fill: 0x00FF00
});
text.anchor.set(0.5, 0.5);
text.x = 2048 / 2;
text.y = 900;
popup.addChild(text);
game.addChild(popup);
LK.effects.flashScreen(0x00ff00, 800);
LK.setTimeout(function () {
if (popup && popup.parent) popup.parent.removeChild(popup);
LK.showYouWin();
}, 1200);
return;
}
// No more logic for this frame
return;
}
// Level 8: Sala de cómputo lógica
if (currentLevel === 8 && game.level8Desks && student) {
// Colisión con escritorios
for (var i = 0; i < game.level8Desks.length; i++) {
var desk = game.level8Desks[i];
if (student.intersects(desk)) {
student.x = student.lastX;
student.y = student.lastY;
student.targetX = student.lastX;
student.targetY = student.lastY;
break;
}
}
// Colisión con paredes (igual que otros niveles)
var leftWall = 40;
var rightWall = 2048 - 40;
var topWall = 40;
var bottomWall = 2732 - 40;
var studentLeft = student.x - 60;
var studentRight = student.x + 60;
var studentTop = student.y - 60;
var studentBottom = student.y + 60;
if (studentLeft < leftWall) student.x = leftWall + 60;
if (studentRight > rightWall) student.x = rightWall - 60;
if (studentTop < topWall) student.y = topWall + 60;
if (studentBottom > bottomWall) student.y = bottomWall - 60;
// Colisión con profesor
if (profesor && student.intersects(profesor)) {
LK.effects.flashScreen(0xff0000, 1000);
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1200);
LK.showGameOver();
return;
}
// Checar si toca la puerta de salida
if (game.level8Door && !game.level8Door._alreadyTriggered && student.intersects(game.level8Door)) {
game.level8Door._alreadyTriggered = true;
var popup = new Container();
var bg = LK.getAsset('furniture', {
width: 2048,
height: 2732,
color: 0x000000,
anchorX: 0,
anchorY: 0
});
bg.alpha = 0.01;
popup.addChild(bg);
var text = new Text2("¡Escapaste de la sala de cómputo!", {
size: 180,
fill: 0x00FF00
});
text.anchor.set(0.5, 0.5);
text.x = 2048 / 2;
text.y = 900;
popup.addChild(text);
game.addChild(popup);
LK.effects.flashScreen(0x00ff00, 800);
LK.setTimeout(function () {
if (popup && popup.parent) popup.parent.removeChild(popup);
LK.showYouWin();
}, 1200);
return;
}
// No más lógica para este frame
return;
}
// Check if student touches the door (minimum contact triggers question)
if (currentLevel === 5 && game.level5Doors && student) {
// Only trigger if not already showing a popup
if (typeof showMathQuestion === "undefined" || !showMathQuestion) {
for (var i = 0; i < game.level5Doors.length; i++) {
var d = game.level5Doors[i];
if (!d._alreadyTriggered && student.intersects(d)) {
d._alreadyTriggered = true;
showMathQuestion = true;
// Show popup: ¿Es esta la puerta correcta?
var popup = new Container();
var bg = LK.getAsset('furniture', {
width: 2048,
height: 2732,
color: 0x000000,
anchorX: 0,
anchorY: 0
});
bg.alpha = 0.01;
popup.addChild(bg);
var msg = d.isCorrectDoor ? "¡Felicidades! Escapaste del colegio." : "¡Esa no es la puerta correcta!";
var color = d.isCorrectDoor ? 0x00FF00 : 0xFF4444;
var text = new Text2(msg, {
size: 180,
fill: color
});
text.anchor.set(0.5, 0.5);
text.x = 2048 / 2;
text.y = 900;
popup.addChild(text);
game.addChild(popup);
if (d.isCorrectDoor) {
LK.effects.flashScreen(0x00ff00, 800);
LK.setTimeout(function () {
if (popup && popup.parent) popup.parent.removeChild(popup);
showMathQuestion = false;
// Advance to level 6 instead of showing win screen
currentLevel = 6;
setupLevel(currentLevel);
}, 1200);
} else {
LK.effects.flashScreen(0xff0000, 800);
LK.setTimeout(function () {
if (popup && popup.parent) popup.parent.removeChild(popup);
showMathQuestion = false;
currentLevel = 1;
setupLevel(currentLevel);
LK.showGameOver();
}, 1200);
}
break;
}
}
}
} else
// Check if student touches the door (minimum contact triggers question)
if ((typeof showMathQuestion === "undefined" || !showMathQuestion) && door && student && !student.lastWasIntersectingDoor && student.intersects(door)) {
showMathQuestion = true;
student.lastWasIntersectingDoor = true;
// Show the math question in-game as a fullscreen overlay with large, high-contrast text and options
if (typeof mathQuestionPopup === "undefined" || !mathQuestionPopup) {
mathQuestionPopup = new Container();
// Fullscreen transparent background (no purple, just to block input)
var bg = LK.getAsset('furniture', {
width: 2048,
height: 2732,
color: 0x000000,
anchorX: 0,
anchorY: 0
});
bg.alpha = 0.01; // almost invisible, just to block input
mathQuestionPopup.addChild(bg);
// Define questions and answers for each level/door
// You can expand this array for more levels/doors
var questions = [{
question: "¿Cuánto es 8 × 6?",
options: [{
text: "1) 42",
correct: false
}, {
text: "2) 48",
correct: true
}, {
text: "3) 54",
correct: false
}]
}, {
question: "¿Cuánto es 7 × 22?",
options: [{
text: "1) 144",
correct: false
}, {
text: "2) 154",
correct: true
}, {
text: "3) 127",
correct: false
}]
}, {
question: "¿Cuánto es 13 × 5?",
options: [{
text: "1) 65",
correct: true
}, {
text: "2) 45",
correct: false
}, {
text: "3) 75",
correct: false
}]
}];
// Pick question by level (default to first if out of range)
var qIndex = typeof currentLevel !== "undefined" && currentLevel - 1 < questions.length ? currentLevel - 1 : 0;
var qData = questions[qIndex];
// Question text - very large, high-contrast, centered
var questionText = new Text2(qData.question, {
size: 180,
fill: 0xFFF700 // bright yellow for contrast
});
questionText.anchor.set(0.5, 0);
questionText.x = 2048 / 2;
questionText.y = 320;
mathQuestionPopup.addChild(questionText);
// Render options
var optionYStart = 800;
var optionYStep = 300;
var optionObjs = [];
for (var i = 0; i < qData.options.length; i++) {
var opt = qData.options[i];
var optObj = new Text2(opt.text, {
size: 140,
fill: 0xFFFFFF
});
optObj.anchor.set(0.5, 0);
optObj.x = 2048 / 2;
optObj.y = optionYStart + i * optionYStep;
mathQuestionPopup.addChild(optObj);
optionObjs.push(optObj);
// Add interaction for options
optObj.interactive = true;
optObj.buttonMode = true;
if (opt.correct) {
optObj.down = function () {
if (typeof mathQuestionTimeout !== "undefined") {
LK.clearTimeout(mathQuestionTimeout);
mathQuestionTimeout = undefined;
}
LK.effects.flashScreen(0x00ff00, 800);
// Open the door: remove it from the game scene
if (door && door.parent) {
door.parent.removeChild(door);
door = null;
}
if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup);
mathQuestionPopup = null;
showMathQuestion = false;
// Advance to next level or win
if (typeof currentLevel !== "undefined" && typeof maxLevel !== "undefined") {
if (currentLevel === 5) {
// If on level 5, advance to level 6 instead of showing win
currentLevel = 6;
setupLevel(currentLevel);
} else if (currentLevel < maxLevel) {
currentLevel += 1;
setupLevel(currentLevel);
} else {
LK.showYouWin();
}
}
};
} else {
optObj.down = function () {
if (typeof mathQuestionTimeout !== "undefined") {
LK.clearTimeout(mathQuestionTimeout);
mathQuestionTimeout = undefined;
}
LK.effects.flashScreen(0xff0000, 800);
// Reset to level 1 after game over
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1000);
LK.showGameOver();
if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup);
mathQuestionPopup = null;
showMathQuestion = false;
};
}
}
// Add timer text
var timeLimit = 10; // seconds
var timeLeft = timeLimit;
var timerText = new Text2("Tiempo: " + timeLeft, {
size: 120,
fill: 0xFF4444
});
timerText.anchor.set(0.5, 0);
timerText.x = 2048 / 2;
timerText.y = 650;
mathQuestionPopup.addChild(timerText);
// Timer logic
if (typeof mathQuestionTimeout !== "undefined") {
LK.clearTimeout(mathQuestionTimeout);
mathQuestionTimeout = undefined;
}
if (typeof mathQuestionInterval !== "undefined") {
LK.clearInterval(mathQuestionInterval);
mathQuestionInterval = undefined;
}
mathQuestionInterval = LK.setInterval(function () {
if (typeof timeLeft !== "undefined") {
timeLeft -= 1;
if (timerText && timerText.setText) timerText.setText("Tiempo: " + timeLeft);
if (timeLeft <= 0) {
// Time's up! Fail the question
if (typeof mathQuestionTimeout !== "undefined") {
LK.clearTimeout(mathQuestionTimeout);
mathQuestionTimeout = undefined;
}
if (typeof mathQuestionInterval !== "undefined") {
LK.clearInterval(mathQuestionInterval);
mathQuestionInterval = undefined;
}
LK.effects.flashScreen(0xff0000, 800);
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1000);
LK.showGameOver();
if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup);
mathQuestionPopup = null;
showMathQuestion = false;
}
}
}, 1000);
// Also set a hard timeout in case interval fails
mathQuestionTimeout = LK.setTimeout(function () {
if (typeof mathQuestionInterval !== "undefined") {
LK.clearInterval(mathQuestionInterval);
mathQuestionInterval = undefined;
}
LK.effects.flashScreen(0xff0000, 800);
currentLevel = 1;
LK.setTimeout(function () {
setupLevel(currentLevel);
}, 1000);
LK.showGameOver();
if (mathQuestionPopup && mathQuestionPopup.parent) mathQuestionPopup.parent.removeChild(mathQuestionPopup);
mathQuestionPopup = null;
showMathQuestion = false;
}, timeLimit * 1000);
// Fullscreen, no pivot, top-left at (0,0)
mathQuestionPopup.x = 0;
mathQuestionPopup.y = 0;
game.addChild(mathQuestionPopup);
}
}
// Track lastX for student for event logic
student.lastX = student.x;
student.lastY = student.y;
// Track last intersection with door for event logic
if (door && student) {
if (typeof student.lastWasIntersectingDoor === "undefined") student.lastWasIntersectingDoor = false;
var nowIntersectingDoor = student.intersects(door);
student.lastWasIntersectingDoor = nowIntersectingDoor;
}
// Camera follows student from behind (3rd person, offset behind student direction)
var dx = student.targetX - student.x;
var dy = student.targetY - student.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var camBehindDist = 350; // Distance behind the student
var camX, camY;
if (dist > 10) {
// Camera is offset behind the student, in the opposite direction of movement
camX = student.x - dx / dist * camBehindDist;
camY = student.y - dy / dist * camBehindDist;
} else {
// If not moving, camera stays slightly above/behind
camX = student.x - camBehindDist;
camY = student.y;
}
// Clamp camera to map bounds
camX = Math.max(0, Math.min(camX, 2048 - 2048));
camY = Math.max(0, Math.min(camY, 2732 - 2732));
game.x = -camX;
game.y = -camY;
};
// On-screen joystick controls for student movement
// Joystick visual base
var joystickBase = LK.getAsset('furniture', {
width: 260,
height: 260,
color: 0x222222,
anchorX: 0.5,
anchorY: 0.5,
x: 260,
y: 2732 - 260
});
joystickBase.alpha = 0.25;
LK.gui.left.addChild(joystickBase);
// Joystick thumb
var joystickThumb = LK.getAsset('furniture', {
width: 120,
height: 120,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0.5,
x: 260,
y: 2732 - 260
});
joystickThumb.alpha = 0.7;
LK.gui.left.addChild(joystickThumb);
var joystickActive = false;
var joystickStartX = 0;
var joystickStartY = 0;
var joystickRadius = 110;
// Helper to convert GUI to game coordinates
function guiToGameCoords(guiX, guiY) {
// For this game, joystick is always at bottom left, so just return as is
return {
x: guiX,
y: guiY
};
}
// Joystick down
joystickBase.interactive = true;
joystickBase.buttonMode = true;
joystickBase.down = function (x, y, obj) {
joystickActive = true;
joystickStartX = joystickBase.x;
joystickStartY = joystickBase.y;
joystickThumb.x = x;
joystickThumb.y = y;
};
// Joystick move
joystickBase.move = function (x, y, obj) {
if (!joystickActive) return;
var dx = x - joystickStartX;
var dy = y - joystickStartY;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > joystickRadius) {
dx = dx / dist * joystickRadius;
dy = dy / dist * joystickRadius;
}
joystickThumb.x = joystickStartX + dx;
joystickThumb.y = joystickStartY + dy;
// Move student target relative to joystick direction
var moveStrength = dist > 20 ? 1 : 0; // Deadzone
if (moveStrength) {
// Map joystick to game area
var moveX = student.x + dx * 3;
var moveY = student.y + dy * 3;
student.targetX = Math.max(60, Math.min(2048 - 60, moveX));
student.targetY = Math.max(60, Math.min(2732 - 60, moveY));
}
};
// Joystick up
joystickBase.up = function (x, y, obj) {
joystickActive = false;
joystickThumb.x = joystickBase.x;
joystickThumb.y = joystickBase.y;
};
// Also allow drag-to-move for desktop or direct touch
game.down = function (x, y, obj) {
if (!gameStarted) return;
if (!joystickActive) {
student.targetX = x;
student.targetY = y;
}
};
game.move = function (x, y, obj) {
if (!gameStarted) return;
if (!joystickActive) {
student.targetX = x;
student.targetY = y;
}
};
game.up = function (x, y, obj) {
// No-op for now
};
Modern App Store icon, high definition, square with rounded corners, for a game titled "San Luis: El Monstruo del Colegio" and with the description "Juego de escondite en el colegio San Luis de Yarumal, donde debes evitar a un profesor monstruoso y escapar del colegio.". No text on icon!
Puerta. In-Game asset. 2d. High contrast. No shadows
Piso liso. In-Game asset. 2d. High contrast. No shadows
Un solo pupitre. In-Game asset. 2d. High contrast. No shadows
Niño pequeño con uniforme blanco y sudadera azul. In-Game asset. 2d. High contrast. No shadows
Suelo de concreto. In-Game asset. 2d. High contrast. No shadows