/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Board class (for classroom board) var Board = Container.expand(function () { var self = Container.call(this); var boardAsset = self.attachAsset('board', { anchorX: 0.5, anchorY: 0.5 }); boardAsset.width = 500; boardAsset.height = 500; boardAsset.color = 0x2d5c3a; // green board return self; }); // Desk class (with hitbox) var Desk = Container.expand(function () { var self = Container.call(this); // Attach a box asset for the desk var deskAsset = self.attachAsset('desk', { anchorX: 0.5, anchorY: 0.5 }); deskAsset.width = 220; deskAsset.height = 120; deskAsset.color = 0xc2b280; // light brown // For hitbox, just use the desk's bounds (self.intersects works) return self; }); // Nerd class var Nerd = Container.expand(function () { var self = Container.call(this); // Attach nerd asset (green ellipse) var nerdAsset = self.attachAsset('nerd', { anchorX: 0.5, anchorY: 0.5 }); nerdAsset.width = 180; nerdAsset.height = 180; nerdAsset.color = 0x6be445; // green nerdAsset.shape = 'ellipse'; // Move nerd to a random position in the classroom (with padding) self.moveToRandom = function () { var pad = 180; var nx = pad + Math.random() * (2048 - 2 * pad); var ny = 400 + Math.random() * (2732 - 800); tween(self, { x: nx, y: ny }, { duration: 900 + Math.random() * 800, easing: tween.easeInOut }); }; return self; }); // Student (Player) class var Student = Container.expand(function () { var self = Container.call(this); // Attach student asset (blue box) var studentAsset = self.attachAsset('student', { anchorX: 0.5, anchorY: 0.5 }); // Set color and size (make bigger) studentAsset.width = 180; studentAsset.height = 180; studentAsset.color = 0x3a8ee6; // blue // For possible future use self.radius = 90; return self; }); // Teacher class var Teacher = Container.expand(function () { var self = Container.call(this); // Attach teacher asset (red rectangle) var teacherAsset = self.attachAsset('teacher', { anchorX: 0.5, anchorY: 0.5 }); teacherAsset.width = 240; teacherAsset.height = 240; teacherAsset.color = 0xe64b3a; // red // For patrol direction self.targetX = 0; self.targetY = 0; self.speed = 8 + Math.random() * 4; // Move teacher to a random position in the classroom (with padding) self.moveToRandom = function () { var pad = 180; self.targetX = pad + Math.random() * (2048 - 2 * pad); self.targetY = 400 + Math.random() * (2732 - 800); self.speed = 8 + Math.random() * 4; }; // Teacher patrol update self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 10) { self.moveToRandom(); } else { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf7e7c1 // light classroom color }); /**** * Game Code ****/ // --- Game variables --- var student = new Student(); var nerd = new Nerd(); var teacher = new Teacher(); var dragNode = null; var lastCopying = false; var copyingTimer = 0; var copyScore = 0; var scoreTxt = null; var lastTeacherCaught = false; var copyDistance = 180; // px, how close to nerd to copy var teacherCatchDistance = 220; // px, how close to teacher to get caught var teacherFOV = Math.PI / 2; // 90 degree field of view var teacherVisionDistance = 500; // px, how far teacher can "see" var nerdMoveTimer = 0; // --- Classroom environment --- // Board at the front var board = new Board(); board.x = 2048 / 2; board.y = 250; game.addChild(board); // Arrange desks in a grid (4 columns x 4 rows) var desks = []; var deskRows = 4; var deskCols = 4; var deskSpacingX = 400; var deskSpacingY = 320; var deskStartX = 384; var deskStartY = 600; for (var row = 0; row < deskRows; row++) { for (var col = 0; col < deskCols; col++) { var desk = new Desk(); desk.x = deskStartX + col * deskSpacingX; desk.y = deskStartY + row * deskSpacingY; desks.push(desk); game.addChild(desk); } } // --- Add elements to game --- game.addChild(nerd); game.addChild(student); game.addChild(teacher); // Add 3 more static students (cannot be copied from), randomizing their desk row/col each round var otherStudents = []; function shuffleArray(arr) { for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } function assignOtherStudentsToDesks() { // Remove previous students from game if any for (var i = 0; i < otherStudents.length; i++) { if (otherStudents[i].parent) { otherStudents[i].parent.removeChild(otherStudents[i]); } } otherStudents = []; // Get all desk indices, shuffle, pick 3 var deskIndices = []; for (var i = 0; i < desks.length; i++) deskIndices.push(i); shuffleArray(deskIndices); for (var i = 0; i < 3; i++) { var deskIdx = deskIndices[i]; var s = new Student(); // Place student at desk center s.x = desks[deskIdx].x; s.y = desks[deskIdx].y; // Make them visually distinct (lighter color) s.children[0].color = 0x7ee6c6; otherStudents.push(s); game.addChild(s); } } // Initial assignment assignOtherStudentsToDesks(); // Initial positions student.x = 2048 / 2; student.y = 2732 - 400; nerd.x = 2048 / 2; nerd.y = 900; teacher.x = 2048 / 2; teacher.y = 400; teacher.moveToRandom(); nerd.moveToRandom(); // --- Score display --- scoreTxt = new Text2('0', { size: 120, fill: 0x222222 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Helper functions --- // Distance between two objects function dist(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; return Math.sqrt(dx * dx + dy * dy); } // Angle from teacher to student function angleTo(a, b) { return Math.atan2(b.y - a.y, b.x - a.x); } // Is student in teacher's field of view and close enough? function teacherCanSeeStudent() { var d = dist(teacher, student); if (d > teacherVisionDistance) return false; // Teacher "looks" toward their movement direction (target) var teacherDir = Math.atan2(teacher.targetY - teacher.y, teacher.targetX - teacher.x); var angleToStu = angleTo(teacher, student); var diff = Math.abs(teacherDir - angleToStu); if (diff > Math.PI) diff = 2 * Math.PI - diff; return diff < teacherFOV / 2; } // --- Dragging logic --- function handleMove(x, y, obj) { if (dragNode) { // Clamp to classroom area (avoid top menu) var pad = 100; var minY = 400; var maxY = 2732 - pad; var minX = pad; var maxX = 2048 - pad; var newX = Math.max(minX, Math.min(maxX, x)); var newY = Math.max(minY, Math.min(maxY, y)); // Temporarily move student to new position to check for desk collision var prevX = dragNode.x; var prevY = dragNode.y; dragNode.x = newX; dragNode.y = newY; var blocked = false; for (var i = 0; i < desks.length; i++) { if (dragNode.intersects(desks[i])) { blocked = true; break; } } if (blocked) { // If blocked, revert to previous position dragNode.x = prevX; dragNode.y = prevY; } else { // Otherwise, keep new position // (already set above) } } } game.move = handleMove; game.down = function (x, y, obj) { // Only allow drag if not caught if (!lastTeacherCaught) { dragNode = student; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // --- Main game update --- // --- Teacher FOV View Asset --- // Use a triangle shape for the FOV cone if (!teacher.fovView) { // Only create once var fovRadius = teacherVisionDistance; var fovAngle = teacherFOV; // Use a triangle asset for the FOV cone // The triangle asset is assumed to be an isosceles triangle pointing right, with base at left, tip at right // We'll scale it to match the FOV angle and vision distance // Default triangle asset is 100x100, base at (0,0)-(0,100), tip at (100,50) var fovAsset = LK.getAsset('triangle', { anchorX: 0, anchorY: 0.5, scaleX: teacherVisionDistance / 100, scaleY: Math.tan(teacherFOV / 2), x: 0, y: 0 }); fovAsset.alpha = 0.18; // transparent fovAsset.tint = 0xff0000; teacher.fovView = fovAsset; teacher.addChild(fovAsset); } // Update FOV cone position, scale, and rotation every frame var fovRadius = teacherVisionDistance; var fovAngle = teacherFOV; teacher.fovView.scaleX = fovRadius / 100; teacher.fovView.scaleY = Math.tan(fovAngle / 2); // Point FOV cone in teacher's facing direction var teacherDir = Math.atan2(teacher.targetY - teacher.y, teacher.targetX - teacher.x); teacher.fovView.rotation = teacherDir; // Place at teacher's center teacher.fovView.x = 0; teacher.fovView.y = 0; game.update = function () { // Move nerd occasionally nerdMoveTimer++; if (nerdMoveTimer > 90 + Math.random() * 60) { nerd.moveToRandom(); nerdMoveTimer = 0; // Re-randomize other students' desk positions each round assignOtherStudentsToDesks(); } // Teacher patrol teacher.update(); // --- Copying logic --- var copying = false; if (dist(student, nerd) < copyDistance) { copying = true; } // If copying, increment timer and score if (copying) { copyingTimer++; if (copyingTimer % 30 === 0) { // every 0.5s copyScore++; scoreTxt.setText(copyScore); // Win condition if (copyScore >= 30) { LK.showYouWin(); } } // Visual feedback: flash nerd green if (!lastCopying) { LK.effects.flashObject(nerd, 0x00ff00, 400); } } lastCopying = copying; // --- Teacher detection logic --- var teacherCaught = false; // If student is copying and teacher can see and is close enough // Also: check if student is inside the FOV cone (distance and angle) if (copying && teacherCanSeeStudent() && dist(teacher, student) < teacherCatchDistance) { teacherCaught = true; } else if (copying) { // Manual FOV cone check (redundant with teacherCanSeeStudent, but explicit for clarity) var d = dist(teacher, student); if (d < teacherVisionDistance) { var teacherDir = Math.atan2(teacher.targetY - teacher.y, teacher.targetX - teacher.x); var angleToStu = angleTo(teacher, student); var diff = Math.abs(teacherDir - angleToStu); if (diff > Math.PI) diff = 2 * Math.PI - diff; if (diff < teacherFOV / 2 && d < teacherCatchDistance) { teacherCaught = true; } } } if (teacherCaught && !lastTeacherCaught) { // Flash screen red LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } lastTeacherCaught = teacherCaught; };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Board class (for classroom board)
var Board = Container.expand(function () {
var self = Container.call(this);
var boardAsset = self.attachAsset('board', {
anchorX: 0.5,
anchorY: 0.5
});
boardAsset.width = 500;
boardAsset.height = 500;
boardAsset.color = 0x2d5c3a; // green board
return self;
});
// Desk class (with hitbox)
var Desk = Container.expand(function () {
var self = Container.call(this);
// Attach a box asset for the desk
var deskAsset = self.attachAsset('desk', {
anchorX: 0.5,
anchorY: 0.5
});
deskAsset.width = 220;
deskAsset.height = 120;
deskAsset.color = 0xc2b280; // light brown
// For hitbox, just use the desk's bounds (self.intersects works)
return self;
});
// Nerd class
var Nerd = Container.expand(function () {
var self = Container.call(this);
// Attach nerd asset (green ellipse)
var nerdAsset = self.attachAsset('nerd', {
anchorX: 0.5,
anchorY: 0.5
});
nerdAsset.width = 180;
nerdAsset.height = 180;
nerdAsset.color = 0x6be445; // green
nerdAsset.shape = 'ellipse';
// Move nerd to a random position in the classroom (with padding)
self.moveToRandom = function () {
var pad = 180;
var nx = pad + Math.random() * (2048 - 2 * pad);
var ny = 400 + Math.random() * (2732 - 800);
tween(self, {
x: nx,
y: ny
}, {
duration: 900 + Math.random() * 800,
easing: tween.easeInOut
});
};
return self;
});
// Student (Player) class
var Student = Container.expand(function () {
var self = Container.call(this);
// Attach student asset (blue box)
var studentAsset = self.attachAsset('student', {
anchorX: 0.5,
anchorY: 0.5
});
// Set color and size (make bigger)
studentAsset.width = 180;
studentAsset.height = 180;
studentAsset.color = 0x3a8ee6; // blue
// For possible future use
self.radius = 90;
return self;
});
// Teacher class
var Teacher = Container.expand(function () {
var self = Container.call(this);
// Attach teacher asset (red rectangle)
var teacherAsset = self.attachAsset('teacher', {
anchorX: 0.5,
anchorY: 0.5
});
teacherAsset.width = 240;
teacherAsset.height = 240;
teacherAsset.color = 0xe64b3a; // red
// For patrol direction
self.targetX = 0;
self.targetY = 0;
self.speed = 8 + Math.random() * 4;
// Move teacher to a random position in the classroom (with padding)
self.moveToRandom = function () {
var pad = 180;
self.targetX = pad + Math.random() * (2048 - 2 * pad);
self.targetY = 400 + Math.random() * (2732 - 800);
self.speed = 8 + Math.random() * 4;
};
// Teacher patrol update
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 10) {
self.moveToRandom();
} else {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf7e7c1 // light classroom color
});
/****
* Game Code
****/
// --- Game variables ---
var student = new Student();
var nerd = new Nerd();
var teacher = new Teacher();
var dragNode = null;
var lastCopying = false;
var copyingTimer = 0;
var copyScore = 0;
var scoreTxt = null;
var lastTeacherCaught = false;
var copyDistance = 180; // px, how close to nerd to copy
var teacherCatchDistance = 220; // px, how close to teacher to get caught
var teacherFOV = Math.PI / 2; // 90 degree field of view
var teacherVisionDistance = 500; // px, how far teacher can "see"
var nerdMoveTimer = 0;
// --- Classroom environment ---
// Board at the front
var board = new Board();
board.x = 2048 / 2;
board.y = 250;
game.addChild(board);
// Arrange desks in a grid (4 columns x 4 rows)
var desks = [];
var deskRows = 4;
var deskCols = 4;
var deskSpacingX = 400;
var deskSpacingY = 320;
var deskStartX = 384;
var deskStartY = 600;
for (var row = 0; row < deskRows; row++) {
for (var col = 0; col < deskCols; col++) {
var desk = new Desk();
desk.x = deskStartX + col * deskSpacingX;
desk.y = deskStartY + row * deskSpacingY;
desks.push(desk);
game.addChild(desk);
}
}
// --- Add elements to game ---
game.addChild(nerd);
game.addChild(student);
game.addChild(teacher);
// Add 3 more static students (cannot be copied from), randomizing their desk row/col each round
var otherStudents = [];
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
function assignOtherStudentsToDesks() {
// Remove previous students from game if any
for (var i = 0; i < otherStudents.length; i++) {
if (otherStudents[i].parent) {
otherStudents[i].parent.removeChild(otherStudents[i]);
}
}
otherStudents = [];
// Get all desk indices, shuffle, pick 3
var deskIndices = [];
for (var i = 0; i < desks.length; i++) deskIndices.push(i);
shuffleArray(deskIndices);
for (var i = 0; i < 3; i++) {
var deskIdx = deskIndices[i];
var s = new Student();
// Place student at desk center
s.x = desks[deskIdx].x;
s.y = desks[deskIdx].y;
// Make them visually distinct (lighter color)
s.children[0].color = 0x7ee6c6;
otherStudents.push(s);
game.addChild(s);
}
}
// Initial assignment
assignOtherStudentsToDesks();
// Initial positions
student.x = 2048 / 2;
student.y = 2732 - 400;
nerd.x = 2048 / 2;
nerd.y = 900;
teacher.x = 2048 / 2;
teacher.y = 400;
teacher.moveToRandom();
nerd.moveToRandom();
// --- Score display ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0x222222
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Helper functions ---
// Distance between two objects
function dist(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Angle from teacher to student
function angleTo(a, b) {
return Math.atan2(b.y - a.y, b.x - a.x);
}
// Is student in teacher's field of view and close enough?
function teacherCanSeeStudent() {
var d = dist(teacher, student);
if (d > teacherVisionDistance) return false;
// Teacher "looks" toward their movement direction (target)
var teacherDir = Math.atan2(teacher.targetY - teacher.y, teacher.targetX - teacher.x);
var angleToStu = angleTo(teacher, student);
var diff = Math.abs(teacherDir - angleToStu);
if (diff > Math.PI) diff = 2 * Math.PI - diff;
return diff < teacherFOV / 2;
}
// --- Dragging logic ---
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to classroom area (avoid top menu)
var pad = 100;
var minY = 400;
var maxY = 2732 - pad;
var minX = pad;
var maxX = 2048 - pad;
var newX = Math.max(minX, Math.min(maxX, x));
var newY = Math.max(minY, Math.min(maxY, y));
// Temporarily move student to new position to check for desk collision
var prevX = dragNode.x;
var prevY = dragNode.y;
dragNode.x = newX;
dragNode.y = newY;
var blocked = false;
for (var i = 0; i < desks.length; i++) {
if (dragNode.intersects(desks[i])) {
blocked = true;
break;
}
}
if (blocked) {
// If blocked, revert to previous position
dragNode.x = prevX;
dragNode.y = prevY;
} else {
// Otherwise, keep new position
// (already set above)
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if not caught
if (!lastTeacherCaught) {
dragNode = student;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Main game update ---
// --- Teacher FOV View Asset ---
// Use a triangle shape for the FOV cone
if (!teacher.fovView) {
// Only create once
var fovRadius = teacherVisionDistance;
var fovAngle = teacherFOV;
// Use a triangle asset for the FOV cone
// The triangle asset is assumed to be an isosceles triangle pointing right, with base at left, tip at right
// We'll scale it to match the FOV angle and vision distance
// Default triangle asset is 100x100, base at (0,0)-(0,100), tip at (100,50)
var fovAsset = LK.getAsset('triangle', {
anchorX: 0,
anchorY: 0.5,
scaleX: teacherVisionDistance / 100,
scaleY: Math.tan(teacherFOV / 2),
x: 0,
y: 0
});
fovAsset.alpha = 0.18; // transparent
fovAsset.tint = 0xff0000;
teacher.fovView = fovAsset;
teacher.addChild(fovAsset);
}
// Update FOV cone position, scale, and rotation every frame
var fovRadius = teacherVisionDistance;
var fovAngle = teacherFOV;
teacher.fovView.scaleX = fovRadius / 100;
teacher.fovView.scaleY = Math.tan(fovAngle / 2);
// Point FOV cone in teacher's facing direction
var teacherDir = Math.atan2(teacher.targetY - teacher.y, teacher.targetX - teacher.x);
teacher.fovView.rotation = teacherDir;
// Place at teacher's center
teacher.fovView.x = 0;
teacher.fovView.y = 0;
game.update = function () {
// Move nerd occasionally
nerdMoveTimer++;
if (nerdMoveTimer > 90 + Math.random() * 60) {
nerd.moveToRandom();
nerdMoveTimer = 0;
// Re-randomize other students' desk positions each round
assignOtherStudentsToDesks();
}
// Teacher patrol
teacher.update();
// --- Copying logic ---
var copying = false;
if (dist(student, nerd) < copyDistance) {
copying = true;
}
// If copying, increment timer and score
if (copying) {
copyingTimer++;
if (copyingTimer % 30 === 0) {
// every 0.5s
copyScore++;
scoreTxt.setText(copyScore);
// Win condition
if (copyScore >= 30) {
LK.showYouWin();
}
}
// Visual feedback: flash nerd green
if (!lastCopying) {
LK.effects.flashObject(nerd, 0x00ff00, 400);
}
}
lastCopying = copying;
// --- Teacher detection logic ---
var teacherCaught = false;
// If student is copying and teacher can see and is close enough
// Also: check if student is inside the FOV cone (distance and angle)
if (copying && teacherCanSeeStudent() && dist(teacher, student) < teacherCatchDistance) {
teacherCaught = true;
} else if (copying) {
// Manual FOV cone check (redundant with teacherCanSeeStudent, but explicit for clarity)
var d = dist(teacher, student);
if (d < teacherVisionDistance) {
var teacherDir = Math.atan2(teacher.targetY - teacher.y, teacher.targetX - teacher.x);
var angleToStu = angleTo(teacher, student);
var diff = Math.abs(teacherDir - angleToStu);
if (diff > Math.PI) diff = 2 * Math.PI - diff;
if (diff < teacherFOV / 2 && d < teacherCatchDistance) {
teacherCaught = true;
}
}
}
if (teacherCaught && !lastTeacherCaught) {
// Flash screen red
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
lastTeacherCaught = teacherCaught;
};
give a teacher. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
nerd emoji. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
The students who does do math exam. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a classroom smartboard. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
student desk. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat