/****
* 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