/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Block (static, can be placed in build mode) var Block = Container.expand(function () { var self = Container.call(this); var blockGfx = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'block'; return self; }); // Bot (friendly or infected) var Bot = Container.expand(function () { var self = Container.call(this); var botGfx = self.attachAsset('bot', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'bot'; self.infected = false; self.cured = false; self.speed = 6; self.direction = 1; self.update = function () { // Find closest infected bot to run away from var closestInfectedBot = null; var closestDistance = Infinity; for (var i = 0; i < infectedBots.length; i++) { if (infectedBots[i].visible) { var dx = infectedBots[i].x - self.x; var dy = infectedBots[i].y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestInfectedBot = infectedBots[i]; } } } // Run away if infected bot is close (within 300 pixels) if (closestInfectedBot && closestDistance < 300) { var dx = self.x - closestInfectedBot.x; // Opposite direction var dy = self.y - closestInfectedBot.y; // Opposite direction var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var newX = self.x + self.speed * dx / distance; var newY = self.y + self.speed * dy / distance; // Check collision with blocks var canMove = true; for (var i = 0; i < worldObjects.length; i++) { if (worldObjects[i].type === 'block') { var tempX = self.x; var tempY = self.y; self.x = newX; self.y = newY; if (self.intersects(worldObjects[i])) { canMove = false; } self.x = tempX; self.y = tempY; if (!canMove) break; } } if (canMove) { self.x = newX; self.y = newY; } } } }; return self; }); // Chip (collectible) var Chip = Container.expand(function () { var self = Container.call(this); var chipGfx = self.attachAsset('chip', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'chip'; self.collected = false; return self; }); // InfectedBot (enemy) var InfectedBot = Container.expand(function () { var self = Container.call(this); var botGfx = self.attachAsset('infectedBot', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'infectedBot'; self.speed = 8; self.direction = 1; self.update = function () { // Find closest normal bot to infect var closestBot = null; var closestDistance = Infinity; for (var i = 0; i < bots.length; i++) { if (bots[i].visible && !bots[i].infected) { var dx = bots[i].x - self.x; var dy = bots[i].y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestBot = bots[i]; } } } // Move towards closest bot if found if (closestBot) { var dx = closestBot.x - self.x; var dy = closestBot.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var newX = self.x + self.speed * dx / distance; var newY = self.y + self.speed * dy / distance; // Check collision with blocks var canMove = true; for (var i = 0; i < worldObjects.length; i++) { if (worldObjects[i].type === 'block') { var tempX = self.x; var tempY = self.y; self.x = newX; self.y = newY; if (self.intersects(worldObjects[i])) { canMove = false; } self.x = tempX; self.y = tempY; if (!canMove) break; } } if (canMove) { self.x = newX; self.y = newY; } } } }; return self; }); // Sign (shows a message) var Sign = Container.expand(function () { var self = Container.call(this); var signGfx = self.attachAsset('sign', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'sign'; self.message = "Welcome!"; return self; }); // Trap (triggers on contact) var Trap = Container.expand(function () { var self = Container.call(this); var trapGfx = self.attachAsset('trap', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'trap'; self.armed = true; self.down = function (x, y, obj) { if (self.armed) { self.armed = false; LK.effects.flashObject(self, 0xff0000, 600); self.alpha = 0.3; LK.getSound('trapTrigger').play(); // Destroy any objects on the trap if (zin && zin.intersects(self)) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); } // Kill any bots on the trap for (var b = bots.length - 1; b >= 0; b--) { if (bots[b].visible && bots[b].intersects(self)) { LK.effects.flashObject(bots[b], 0xff0000, 400); bots[b].destroy(); bots.splice(b, 1); var objIndex = worldObjects.indexOf(bots[b]); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } } } // Kill any infected bots on the trap for (var ib = infectedBots.length - 1; ib >= 0; ib--) { if (infectedBots[ib].visible && infectedBots[ib].intersects(self)) { LK.effects.flashObject(infectedBots[ib], 0xff0000, 400); infectedBots[ib].destroy(); infectedBots.splice(ib, 1); var objIndex = worldObjects.indexOf(infectedBots[ib]); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } } } } else { // Trap is closed, clicking again opens it self.armed = true; LK.effects.flashObject(self, 0x00ff00, 600); self.alpha = 1; LK.getSound('trapTrigger').play(); } }; return self; }); // Wire (for future wiring, just visual for now) var Wire = Container.expand(function () { var self = Container.call(this); var wireGfx = self.attachAsset('wire', { anchorX: 0.5, anchorY: 0.0 }); self.type = 'wire'; return self; }); // Zin (player robot) var Zin = Container.expand(function () { var self = Container.call(this); var zinGfx = self.attachAsset('zin', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'zin'; self.speed = 12; self.targetX = self.x; self.targetY = self.y; self.moving = false; self.update = function () { // Check for infected bots in vision range (400 pixels) var closestInfectedBot = null; var closestDistance = Infinity; var visionRange = 400; for (var i = 0; i < infectedBots.length; i++) { if (infectedBots[i].visible) { var dx = infectedBots[i].x - self.x; var dy = infectedBots[i].y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < visionRange && distance < closestDistance) { closestDistance = distance; closestInfectedBot = infectedBots[i]; } } } // If infected bot spotted, follow it (overrides other movement) if (closestInfectedBot) { self.targetX = closestInfectedBot.x; self.targetY = closestInfectedBot.y; self.speed = 15; // Move faster when following infected bots self.moving = true; } // Move towards target if needed if (self.moving) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.speed) { var newX = self.x + self.speed * dx / dist; var newY = self.y + self.speed * dy / dist; // Check collision with blocks var canMove = true; for (var i = 0; i < worldObjects.length; i++) { if (worldObjects[i].type === 'block') { var tempX = self.x; var tempY = self.y; self.x = newX; self.y = newY; if (self.intersects(worldObjects[i])) { canMove = false; } self.x = tempX; self.y = tempY; if (!canMove) break; } } if (canMove) { self.x = newX; self.y = newY; } else { self.moving = false; } } else { self.x = self.targetX; self.y = self.targetY; self.moving = false; } } else if (!closestInfectedBot) { // Random slow walking when not moving to a specific target and no infected bots in sight // 1 in 120 chance per frame to start random walking (about every 2 seconds at 60fps) if (Math.random() < 0.008333) { // Pick a random direction and slow speed var angle = Math.random() * 2 * Math.PI; var slowSpeed = 2; // Much slower than normal speed of 12 var moveDistance = 50 + Math.random() * 100; // Random distance between 50-150 pixels self.targetX = self.x + Math.cos(angle) * moveDistance; self.targetY = self.y + Math.sin(angle) * moveDistance; // Keep within game bounds self.targetX = Math.max(100, Math.min(1948, self.targetX)); self.targetY = Math.max(100, Math.min(2632, self.targetY)); self.speed = slowSpeed; self.moving = true; } } // Update speech bubble position if it exists if (self.speechBubble) { self.speechBubble.x = self.x; self.speechBubble.y = self.y - 120; } }; // Array of positive messages Zin can say self.goodMessages = ["Great job!", "Awesome work!", "You're doing amazing!", "Keep it up!", "Fantastic!", "Well done!", "Excellent!", "Outstanding!", "Perfect!", "Brilliant!", "Nice one!", "Wonderful!", "Super!", "Incredible!", "Amazing!"]; // Current speech bubble text object self.speechBubble = null; self.speechTimer = null; // Method to make Zin say something good self.sayMessage = function () { var randomIndex = Math.floor(Math.random() * self.goodMessages.length); var message = self.goodMessages[randomIndex]; // Remove existing speech bubble if any if (self.speechBubble) { self.speechBubble.destroy(); self.speechBubble = null; } if (self.speechTimer) { LK.clearTimeout(self.speechTimer); self.speechTimer = null; } // Create new speech bubble self.speechBubble = new Text2(message, { size: 40, fill: 0xFFD700 }); self.speechBubble.anchor.set(0.5, 1); self.speechBubble.x = self.x; self.speechBubble.y = self.y - 120; game.addChild(self.speechBubble); // Remove speech bubble after 2 seconds self.speechTimer = LK.setTimeout(function () { if (self.speechBubble) { self.speechBubble.destroy(); self.speechBubble = null; } self.speechTimer = null; }, 2000); }; self.intersectsPoint = function (x, y) { // Simple bounding box check for Zin var left = self.x - self.width / 2; var right = self.x + self.width / 2; var top = self.y - self.height / 2; var bottom = self.y + self.height / 2; return x >= left && x <= right && y >= top && y <= bottom; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222233 }); /**** * Game Code ****/ // Sounds // Robots // World blocks // --- Game State --- var buildMode = true; // true: Build Mode, false: Play Mode var selectedTool = 'block'; // block, chip, trap, sign, wire, zin, bot, infectedBot var worldObjects = []; // All placed objects var chips = []; var traps = []; var bots = []; var infectedBots = []; var zin = null; var draggingObj = null; var dragOffsetX = 0; var dragOffsetY = 0; var lastIntersectingTrap = false; var collectedChips = 0; var totalChips = 0; // --- UI --- var modeTxt = new Text2('Build Mode', { size: 90, fill: "#fff" }); modeTxt.anchor.set(0.5, 0); LK.gui.top.addChild(modeTxt); var toolTxt = new Text2('Tool: Block', { size: 60, fill: "#fff" }); toolTxt.anchor.set(0.5, 0); LK.gui.top.addChild(toolTxt); var chipTxt = new Text2('Chips: 0/0', { size: 80, fill: 0x00E0FF }); chipTxt.anchor.set(0.5, 0); LK.gui.top.addChild(chipTxt); modeTxt.y = 120; toolTxt.y = 220; chipTxt.y = 320; // --- Menu Header --- var menuHeader = new Text2('OBJECT MENU', { size: 55, fill: 0xFF6B6B }); menuHeader.anchor.set(0, 0.5); menuHeader.x = 150; menuHeader.y = 450; LK.gui.top.addChild(menuHeader); // --- Instructions --- var instructionText = new Text2('Tap objects to select • Drag to move in Build Mode', { size: 35, fill: 0xCCCCCC }); instructionText.anchor.set(0.5, 0); instructionText.x = 1024; instructionText.y = 380; LK.gui.top.addChild(instructionText); // --- Organized Object Menu --- var menuCategories = { 'Building': ['block', 'wire', 'eraser'], 'Characters': ['zin', 'bot', 'infectedBot'], 'Interactive': ['chip', 'trap', 'sign'] }; var toolButtons = []; var categoryTitles = []; var menuStartY = 500; var currentY = menuStartY; // Create category sections var categoryKeys = Object.keys(menuCategories); for (var catIndex = 0; catIndex < categoryKeys.length; catIndex++) { var categoryName = categoryKeys[catIndex]; var items = menuCategories[categoryName]; // Create category title var categoryTitle = new Text2(categoryName, { size: 50, fill: 0xFFD700 }); categoryTitle.anchor.set(0, 0.5); categoryTitle.x = 150; categoryTitle.y = currentY; LK.gui.top.addChild(categoryTitle); categoryTitles.push(categoryTitle); currentY += 60; // Create buttons for items in this category for (var i = 0; i < items.length; i++) { var toolName = items[i]; var btn = new Text2('• ' + toolName.charAt(0).toUpperCase() + toolName.slice(1), { size: 45, fill: "#fff" }); btn.anchor.set(0, 0.5); btn.x = 200; btn.y = currentY; // Create closure for tool selection (function (tool) { btn.down = function (x, y, obj) { selectedTool = tool; toolTxt.setText('Tool: ' + tool.charAt(0).toUpperCase() + tool.slice(1)); // Update button colors to show selection for (var b = 0; b < toolButtons.length; b++) { toolButtons[b].tint = 0xFFFFFF; } btn.tint = 0x00FF00; }; })(toolName); LK.gui.top.addChild(btn); toolButtons.push(btn); currentY += 50; } currentY += 20; // Extra space between categories } // Set initial selection highlight if (toolButtons.length > 0) { toolButtons[0].tint = 0x00FF00; } // --- Mode Switch Button --- var modeBtn = new Text2('Switch to Play', { size: 70, fill: "#ff0" }); modeBtn.anchor.set(0.5, 0.5); modeBtn.x = 2048 - 300; modeBtn.y = 420; modeBtn.down = function (x, y, obj) { buildMode = !buildMode; if (buildMode) { modeTxt.setText('Build Mode'); modeBtn.setText('Switch to Play'); } else { modeTxt.setText('Play Mode'); modeBtn.setText('Switch to Build'); startPlayMode(); } }; LK.gui.top.addChild(modeBtn); // --- Helper: Place object at (x, y) --- function placeObject(tool, x, y) { var obj = null; if (tool === 'block') { obj = new Block(); obj.x = x; obj.y = y; worldObjects.push(obj); game.addChild(obj); } else if (tool === 'chip') { obj = new Chip(); obj.x = x; obj.y = y; chips.push(obj); worldObjects.push(obj); game.addChild(obj); totalChips++; chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips); } else if (tool === 'trap') { obj = new Trap(); obj.x = x; obj.y = y; traps.push(obj); worldObjects.push(obj); game.addChild(obj); } else if (tool === 'sign') { obj = new Sign(); obj.x = x; obj.y = y; worldObjects.push(obj); game.addChild(obj); } else if (tool === 'wire') { obj = new Wire(); obj.x = x; obj.y = y; worldObjects.push(obj); game.addChild(obj); } else if (tool === 'zin') { if (!zin) { zin = new Zin(); zin.x = x; zin.y = y; game.addChild(zin); } else { zin.x = x; zin.y = y; } } else if (tool === 'bot') { var bot = new Bot(); bot.x = x; bot.y = y; bots.push(bot); worldObjects.push(bot); game.addChild(bot); } else if (tool === 'infectedBot') { var ibot = new InfectedBot(); ibot.x = x; ibot.y = y; infectedBots.push(ibot); worldObjects.push(ibot); game.addChild(ibot); } else if (tool === 'eraser') { // Eraser doesn't create objects, handled in game.down } return obj; } // --- Build Mode: Place or drag objects --- game.down = function (x, y, obj) { if (buildMode) { // Check if using eraser tool if (selectedTool === 'eraser') { // Find and remove objects at click position for (var i = worldObjects.length - 1; i >= 0; i--) { var o = worldObjects[i]; if (o.intersectsPoint(x, y)) { // Remove from specific arrays if (o.type === 'chip') { var chipIndex = chips.indexOf(o); if (chipIndex > -1) { chips.splice(chipIndex, 1); totalChips--; chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips); } } else if (o.type === 'trap') { var trapIndex = traps.indexOf(o); if (trapIndex > -1) { traps.splice(trapIndex, 1); } } else if (o.type === 'bot') { var botIndex = bots.indexOf(o); if (botIndex > -1) { bots.splice(botIndex, 1); } } else if (o.type === 'infectedBot') { var ibotIndex = infectedBots.indexOf(o); if (ibotIndex > -1) { infectedBots.splice(ibotIndex, 1); } } // Remove from worldObjects and destroy o.destroy(); worldObjects.splice(i, 1); return; } } // Check if erasing Zin if (zin && zin.intersectsPoint && zin.intersectsPoint(x, y)) { zin.destroy(); zin = null; return; } return; } // Check if touching an object to drag for (var i = worldObjects.length - 1; i >= 0; i--) { var o = worldObjects[i]; if (o.intersectsPoint(x, y)) { draggingObj = o; dragOffsetX = x - o.x; dragOffsetY = y - o.y; return; } } // Place new object placeObject(selectedTool, x, y); } else { // Play mode: move Zin if (zin) { // Check if target position would collide with blocks var canMoveTo = true; var tempX = zin.x; var tempY = zin.y; zin.x = x; zin.y = y; for (var i = 0; i < worldObjects.length; i++) { if (worldObjects[i].type === 'block' && zin.intersects(worldObjects[i])) { canMoveTo = false; break; } } zin.x = tempX; zin.y = tempY; if (canMoveTo) { zin.targetX = x; zin.targetY = y; zin.speed = 12; // Reset to normal speed when manually controlled zin.moving = true; } } } }; game.move = function (x, y, obj) { if (buildMode && draggingObj) { draggingObj.x = x - dragOffsetX; draggingObj.y = y - dragOffsetY; } }; game.up = function (x, y, obj) { draggingObj = null; }; // --- Play Mode: Reset state, count chips, etc --- function startPlayMode() { collectedChips = 0; totalChips = 0; for (var i = 0; i < chips.length; i++) { chips[i].collected = false; chips[i].visible = true; totalChips++; } chipTxt.setText('Chips: 0/' + totalChips); // Reset traps for (var i = 0; i < traps.length; i++) { traps[i].armed = true; traps[i].alpha = 1; } // Reset bots for (var i = 0; i < bots.length; i++) { bots[i].cured = false; bots[i].visible = true; } // Reset infected bots for (var i = 0; i < infectedBots.length; i++) { infectedBots[i].visible = true; } // Place Zin at start if not present if (!zin) { zin = new Zin(); zin.x = 400; zin.y = 400; game.addChild(zin); } zin.moving = false; zin.targetX = zin.x; zin.targetY = zin.y; } // --- Main Game Loop --- game.update = function () { // Only update in play mode if (!buildMode) { // Update Zin if (zin) { zin.update(); // Randomly make Zin say good stuff (1 in 300 chance per frame) if (Math.random() < 0.003333) { zin.sayMessage(); } } // Update bots for (var i = 0; i < bots.length; i++) { bots[i].update(); } for (var i = 0; i < infectedBots.length; i++) { infectedBots[i].update(); } // Zin collects chips for (var i = 0; i < chips.length; i++) { var chip = chips[i]; if (!chip.collected && zin && zin.intersects(chip)) { chip.collected = true; chip.visible = false; collectedChips++; chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips); LK.getSound('chipCollect').play(); // Make Zin say something good zin.sayMessage(); // Win if all chips collected if (collectedChips >= totalChips && totalChips > 0) { LK.showYouWin(); } } } // Infected bots touching chips turn into normal bots for (var i = 0; i < chips.length; i++) { var chip = chips[i]; if (!chip.collected) { for (var ib = infectedBots.length - 1; ib >= 0; ib--) { var infectedBot = infectedBots[ib]; if (infectedBot.visible && infectedBot.intersects(chip)) { // Convert infected bot to normal bot var newBot = new Bot(); newBot.x = infectedBot.x; newBot.y = infectedBot.y; bots.push(newBot); worldObjects.push(newBot); game.addChild(newBot); // Remove infected bot infectedBot.destroy(); infectedBots.splice(ib, 1); var objIndex = worldObjects.indexOf(infectedBot); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } // Collect the chip chip.collected = true; chip.visible = false; collectedChips++; chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips); LK.getSound('botCured').play(); // Win if all chips collected if (collectedChips >= totalChips && totalChips > 0) { LK.showYouWin(); } break; } } } } // Check if any robot (Zin, bots, or infected bots) triggers traps for (var i = 0; i < traps.length; i++) { var trap = traps[i]; var robotOnTrap = false; // Check Zin if (trap.armed && zin && zin.intersects(trap)) { robotOnTrap = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); } // Check friendly bots for (var b = 0; b < bots.length; b++) { if (trap.armed && bots[b].visible && bots[b].intersects(trap)) { robotOnTrap = true; break; } } // Check infected bots for (var ib = 0; ib < infectedBots.length; ib++) { if (trap.armed && infectedBots[ib].visible && infectedBots[ib].intersects(trap)) { robotOnTrap = true; break; } } // Close trap if any robot is on it if (robotOnTrap && trap.armed) { trap.armed = false; LK.effects.flashObject(trap, 0xff0000, 600); trap.alpha = 0.3; LK.getSound('trapTrigger').play(); // Kill any bots on the trap for (var b = bots.length - 1; b >= 0; b--) { if (bots[b].visible && bots[b].intersects(trap)) { LK.effects.flashObject(bots[b], 0xff0000, 400); bots[b].destroy(); bots.splice(b, 1); // Remove from worldObjects array var objIndex = worldObjects.indexOf(bots[b]); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } } } // Kill any infected bots on the trap for (var ib = infectedBots.length - 1; ib >= 0; ib--) { if (infectedBots[ib].visible && infectedBots[ib].intersects(trap)) { LK.effects.flashObject(infectedBots[ib], 0xff0000, 400); infectedBots[ib].destroy(); infectedBots.splice(ib, 1); // Remove from worldObjects array var objIndex = worldObjects.indexOf(infectedBots[ib]); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } } } } } // Normal bots get infected when touching infected bots for (var b = bots.length - 1; b >= 0; b--) { var normalBot = bots[b]; if (normalBot.visible && !normalBot.infected) { for (var ib = 0; ib < infectedBots.length; ib++) { var infectedBot = infectedBots[ib]; if (infectedBot.visible && normalBot.intersects(infectedBot)) { // Convert normal bot to infected bot var newInfectedBot = new InfectedBot(); newInfectedBot.x = normalBot.x; newInfectedBot.y = normalBot.y; infectedBots.push(newInfectedBot); worldObjects.push(newInfectedBot); game.addChild(newInfectedBot); // Remove normal bot normalBot.destroy(); bots.splice(b, 1); var objIndex = worldObjects.indexOf(normalBot); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } LK.getSound('robot').play(); break; } } } } // Zin cures infected bots by making them disappear for (var i = infectedBots.length - 1; i >= 0; i--) { var ibot = infectedBots[i]; if (ibot.visible && zin && zin.intersects(ibot)) { // Remove infected bot completely ibot.destroy(); infectedBots.splice(i, 1); var objIndex = worldObjects.indexOf(ibot); if (objIndex > -1) { worldObjects.splice(objIndex, 1); } LK.getSound('botCured').play(); // Make Zin say something good zin.sayMessage(); } } } }; // --- Utility: intersectsPoint for drag selection --- Container.prototype.intersectsPoint = function (x, y) { // Simple bounding box check var left = this.x - this.width / 2; var right = this.x + this.width / 2; var top = this.y - this.height / 2; var bottom = this.y + this.height / 2; return x >= left && x <= right && y >= top && y <= bottom; };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Block (static, can be placed in build mode)
var Block = Container.expand(function () {
var self = Container.call(this);
var blockGfx = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'block';
return self;
});
// Bot (friendly or infected)
var Bot = Container.expand(function () {
var self = Container.call(this);
var botGfx = self.attachAsset('bot', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'bot';
self.infected = false;
self.cured = false;
self.speed = 6;
self.direction = 1;
self.update = function () {
// Find closest infected bot to run away from
var closestInfectedBot = null;
var closestDistance = Infinity;
for (var i = 0; i < infectedBots.length; i++) {
if (infectedBots[i].visible) {
var dx = infectedBots[i].x - self.x;
var dy = infectedBots[i].y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestInfectedBot = infectedBots[i];
}
}
}
// Run away if infected bot is close (within 300 pixels)
if (closestInfectedBot && closestDistance < 300) {
var dx = self.x - closestInfectedBot.x; // Opposite direction
var dy = self.y - closestInfectedBot.y; // Opposite direction
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var newX = self.x + self.speed * dx / distance;
var newY = self.y + self.speed * dy / distance;
// Check collision with blocks
var canMove = true;
for (var i = 0; i < worldObjects.length; i++) {
if (worldObjects[i].type === 'block') {
var tempX = self.x;
var tempY = self.y;
self.x = newX;
self.y = newY;
if (self.intersects(worldObjects[i])) {
canMove = false;
}
self.x = tempX;
self.y = tempY;
if (!canMove) break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
}
};
return self;
});
// Chip (collectible)
var Chip = Container.expand(function () {
var self = Container.call(this);
var chipGfx = self.attachAsset('chip', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'chip';
self.collected = false;
return self;
});
// InfectedBot (enemy)
var InfectedBot = Container.expand(function () {
var self = Container.call(this);
var botGfx = self.attachAsset('infectedBot', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'infectedBot';
self.speed = 8;
self.direction = 1;
self.update = function () {
// Find closest normal bot to infect
var closestBot = null;
var closestDistance = Infinity;
for (var i = 0; i < bots.length; i++) {
if (bots[i].visible && !bots[i].infected) {
var dx = bots[i].x - self.x;
var dy = bots[i].y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestBot = bots[i];
}
}
}
// Move towards closest bot if found
if (closestBot) {
var dx = closestBot.x - self.x;
var dy = closestBot.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var newX = self.x + self.speed * dx / distance;
var newY = self.y + self.speed * dy / distance;
// Check collision with blocks
var canMove = true;
for (var i = 0; i < worldObjects.length; i++) {
if (worldObjects[i].type === 'block') {
var tempX = self.x;
var tempY = self.y;
self.x = newX;
self.y = newY;
if (self.intersects(worldObjects[i])) {
canMove = false;
}
self.x = tempX;
self.y = tempY;
if (!canMove) break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
}
};
return self;
});
// Sign (shows a message)
var Sign = Container.expand(function () {
var self = Container.call(this);
var signGfx = self.attachAsset('sign', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'sign';
self.message = "Welcome!";
return self;
});
// Trap (triggers on contact)
var Trap = Container.expand(function () {
var self = Container.call(this);
var trapGfx = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'trap';
self.armed = true;
self.down = function (x, y, obj) {
if (self.armed) {
self.armed = false;
LK.effects.flashObject(self, 0xff0000, 600);
self.alpha = 0.3;
LK.getSound('trapTrigger').play();
// Destroy any objects on the trap
if (zin && zin.intersects(self)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// Kill any bots on the trap
for (var b = bots.length - 1; b >= 0; b--) {
if (bots[b].visible && bots[b].intersects(self)) {
LK.effects.flashObject(bots[b], 0xff0000, 400);
bots[b].destroy();
bots.splice(b, 1);
var objIndex = worldObjects.indexOf(bots[b]);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
}
}
// Kill any infected bots on the trap
for (var ib = infectedBots.length - 1; ib >= 0; ib--) {
if (infectedBots[ib].visible && infectedBots[ib].intersects(self)) {
LK.effects.flashObject(infectedBots[ib], 0xff0000, 400);
infectedBots[ib].destroy();
infectedBots.splice(ib, 1);
var objIndex = worldObjects.indexOf(infectedBots[ib]);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
}
}
} else {
// Trap is closed, clicking again opens it
self.armed = true;
LK.effects.flashObject(self, 0x00ff00, 600);
self.alpha = 1;
LK.getSound('trapTrigger').play();
}
};
return self;
});
// Wire (for future wiring, just visual for now)
var Wire = Container.expand(function () {
var self = Container.call(this);
var wireGfx = self.attachAsset('wire', {
anchorX: 0.5,
anchorY: 0.0
});
self.type = 'wire';
return self;
});
// Zin (player robot)
var Zin = Container.expand(function () {
var self = Container.call(this);
var zinGfx = self.attachAsset('zin', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'zin';
self.speed = 12;
self.targetX = self.x;
self.targetY = self.y;
self.moving = false;
self.update = function () {
// Check for infected bots in vision range (400 pixels)
var closestInfectedBot = null;
var closestDistance = Infinity;
var visionRange = 400;
for (var i = 0; i < infectedBots.length; i++) {
if (infectedBots[i].visible) {
var dx = infectedBots[i].x - self.x;
var dy = infectedBots[i].y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < visionRange && distance < closestDistance) {
closestDistance = distance;
closestInfectedBot = infectedBots[i];
}
}
}
// If infected bot spotted, follow it (overrides other movement)
if (closestInfectedBot) {
self.targetX = closestInfectedBot.x;
self.targetY = closestInfectedBot.y;
self.speed = 15; // Move faster when following infected bots
self.moving = true;
}
// Move towards target if needed
if (self.moving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.speed) {
var newX = self.x + self.speed * dx / dist;
var newY = self.y + self.speed * dy / dist;
// Check collision with blocks
var canMove = true;
for (var i = 0; i < worldObjects.length; i++) {
if (worldObjects[i].type === 'block') {
var tempX = self.x;
var tempY = self.y;
self.x = newX;
self.y = newY;
if (self.intersects(worldObjects[i])) {
canMove = false;
}
self.x = tempX;
self.y = tempY;
if (!canMove) break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
} else {
self.moving = false;
}
} else {
self.x = self.targetX;
self.y = self.targetY;
self.moving = false;
}
} else if (!closestInfectedBot) {
// Random slow walking when not moving to a specific target and no infected bots in sight
// 1 in 120 chance per frame to start random walking (about every 2 seconds at 60fps)
if (Math.random() < 0.008333) {
// Pick a random direction and slow speed
var angle = Math.random() * 2 * Math.PI;
var slowSpeed = 2; // Much slower than normal speed of 12
var moveDistance = 50 + Math.random() * 100; // Random distance between 50-150 pixels
self.targetX = self.x + Math.cos(angle) * moveDistance;
self.targetY = self.y + Math.sin(angle) * moveDistance;
// Keep within game bounds
self.targetX = Math.max(100, Math.min(1948, self.targetX));
self.targetY = Math.max(100, Math.min(2632, self.targetY));
self.speed = slowSpeed;
self.moving = true;
}
}
// Update speech bubble position if it exists
if (self.speechBubble) {
self.speechBubble.x = self.x;
self.speechBubble.y = self.y - 120;
}
};
// Array of positive messages Zin can say
self.goodMessages = ["Great job!", "Awesome work!", "You're doing amazing!", "Keep it up!", "Fantastic!", "Well done!", "Excellent!", "Outstanding!", "Perfect!", "Brilliant!", "Nice one!", "Wonderful!", "Super!", "Incredible!", "Amazing!"];
// Current speech bubble text object
self.speechBubble = null;
self.speechTimer = null;
// Method to make Zin say something good
self.sayMessage = function () {
var randomIndex = Math.floor(Math.random() * self.goodMessages.length);
var message = self.goodMessages[randomIndex];
// Remove existing speech bubble if any
if (self.speechBubble) {
self.speechBubble.destroy();
self.speechBubble = null;
}
if (self.speechTimer) {
LK.clearTimeout(self.speechTimer);
self.speechTimer = null;
}
// Create new speech bubble
self.speechBubble = new Text2(message, {
size: 40,
fill: 0xFFD700
});
self.speechBubble.anchor.set(0.5, 1);
self.speechBubble.x = self.x;
self.speechBubble.y = self.y - 120;
game.addChild(self.speechBubble);
// Remove speech bubble after 2 seconds
self.speechTimer = LK.setTimeout(function () {
if (self.speechBubble) {
self.speechBubble.destroy();
self.speechBubble = null;
}
self.speechTimer = null;
}, 2000);
};
self.intersectsPoint = function (x, y) {
// Simple bounding box check for Zin
var left = self.x - self.width / 2;
var right = self.x + self.width / 2;
var top = self.y - self.height / 2;
var bottom = self.y + self.height / 2;
return x >= left && x <= right && y >= top && y <= bottom;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222233
});
/****
* Game Code
****/
// Sounds
// Robots
// World blocks
// --- Game State ---
var buildMode = true; // true: Build Mode, false: Play Mode
var selectedTool = 'block'; // block, chip, trap, sign, wire, zin, bot, infectedBot
var worldObjects = []; // All placed objects
var chips = [];
var traps = [];
var bots = [];
var infectedBots = [];
var zin = null;
var draggingObj = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var lastIntersectingTrap = false;
var collectedChips = 0;
var totalChips = 0;
// --- UI ---
var modeTxt = new Text2('Build Mode', {
size: 90,
fill: "#fff"
});
modeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(modeTxt);
var toolTxt = new Text2('Tool: Block', {
size: 60,
fill: "#fff"
});
toolTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(toolTxt);
var chipTxt = new Text2('Chips: 0/0', {
size: 80,
fill: 0x00E0FF
});
chipTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(chipTxt);
modeTxt.y = 120;
toolTxt.y = 220;
chipTxt.y = 320;
// --- Menu Header ---
var menuHeader = new Text2('OBJECT MENU', {
size: 55,
fill: 0xFF6B6B
});
menuHeader.anchor.set(0, 0.5);
menuHeader.x = 150;
menuHeader.y = 450;
LK.gui.top.addChild(menuHeader);
// --- Instructions ---
var instructionText = new Text2('Tap objects to select • Drag to move in Build Mode', {
size: 35,
fill: 0xCCCCCC
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 1024;
instructionText.y = 380;
LK.gui.top.addChild(instructionText);
// --- Organized Object Menu ---
var menuCategories = {
'Building': ['block', 'wire', 'eraser'],
'Characters': ['zin', 'bot', 'infectedBot'],
'Interactive': ['chip', 'trap', 'sign']
};
var toolButtons = [];
var categoryTitles = [];
var menuStartY = 500;
var currentY = menuStartY;
// Create category sections
var categoryKeys = Object.keys(menuCategories);
for (var catIndex = 0; catIndex < categoryKeys.length; catIndex++) {
var categoryName = categoryKeys[catIndex];
var items = menuCategories[categoryName];
// Create category title
var categoryTitle = new Text2(categoryName, {
size: 50,
fill: 0xFFD700
});
categoryTitle.anchor.set(0, 0.5);
categoryTitle.x = 150;
categoryTitle.y = currentY;
LK.gui.top.addChild(categoryTitle);
categoryTitles.push(categoryTitle);
currentY += 60;
// Create buttons for items in this category
for (var i = 0; i < items.length; i++) {
var toolName = items[i];
var btn = new Text2('• ' + toolName.charAt(0).toUpperCase() + toolName.slice(1), {
size: 45,
fill: "#fff"
});
btn.anchor.set(0, 0.5);
btn.x = 200;
btn.y = currentY;
// Create closure for tool selection
(function (tool) {
btn.down = function (x, y, obj) {
selectedTool = tool;
toolTxt.setText('Tool: ' + tool.charAt(0).toUpperCase() + tool.slice(1));
// Update button colors to show selection
for (var b = 0; b < toolButtons.length; b++) {
toolButtons[b].tint = 0xFFFFFF;
}
btn.tint = 0x00FF00;
};
})(toolName);
LK.gui.top.addChild(btn);
toolButtons.push(btn);
currentY += 50;
}
currentY += 20; // Extra space between categories
}
// Set initial selection highlight
if (toolButtons.length > 0) {
toolButtons[0].tint = 0x00FF00;
}
// --- Mode Switch Button ---
var modeBtn = new Text2('Switch to Play', {
size: 70,
fill: "#ff0"
});
modeBtn.anchor.set(0.5, 0.5);
modeBtn.x = 2048 - 300;
modeBtn.y = 420;
modeBtn.down = function (x, y, obj) {
buildMode = !buildMode;
if (buildMode) {
modeTxt.setText('Build Mode');
modeBtn.setText('Switch to Play');
} else {
modeTxt.setText('Play Mode');
modeBtn.setText('Switch to Build');
startPlayMode();
}
};
LK.gui.top.addChild(modeBtn);
// --- Helper: Place object at (x, y) ---
function placeObject(tool, x, y) {
var obj = null;
if (tool === 'block') {
obj = new Block();
obj.x = x;
obj.y = y;
worldObjects.push(obj);
game.addChild(obj);
} else if (tool === 'chip') {
obj = new Chip();
obj.x = x;
obj.y = y;
chips.push(obj);
worldObjects.push(obj);
game.addChild(obj);
totalChips++;
chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips);
} else if (tool === 'trap') {
obj = new Trap();
obj.x = x;
obj.y = y;
traps.push(obj);
worldObjects.push(obj);
game.addChild(obj);
} else if (tool === 'sign') {
obj = new Sign();
obj.x = x;
obj.y = y;
worldObjects.push(obj);
game.addChild(obj);
} else if (tool === 'wire') {
obj = new Wire();
obj.x = x;
obj.y = y;
worldObjects.push(obj);
game.addChild(obj);
} else if (tool === 'zin') {
if (!zin) {
zin = new Zin();
zin.x = x;
zin.y = y;
game.addChild(zin);
} else {
zin.x = x;
zin.y = y;
}
} else if (tool === 'bot') {
var bot = new Bot();
bot.x = x;
bot.y = y;
bots.push(bot);
worldObjects.push(bot);
game.addChild(bot);
} else if (tool === 'infectedBot') {
var ibot = new InfectedBot();
ibot.x = x;
ibot.y = y;
infectedBots.push(ibot);
worldObjects.push(ibot);
game.addChild(ibot);
} else if (tool === 'eraser') {
// Eraser doesn't create objects, handled in game.down
}
return obj;
}
// --- Build Mode: Place or drag objects ---
game.down = function (x, y, obj) {
if (buildMode) {
// Check if using eraser tool
if (selectedTool === 'eraser') {
// Find and remove objects at click position
for (var i = worldObjects.length - 1; i >= 0; i--) {
var o = worldObjects[i];
if (o.intersectsPoint(x, y)) {
// Remove from specific arrays
if (o.type === 'chip') {
var chipIndex = chips.indexOf(o);
if (chipIndex > -1) {
chips.splice(chipIndex, 1);
totalChips--;
chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips);
}
} else if (o.type === 'trap') {
var trapIndex = traps.indexOf(o);
if (trapIndex > -1) {
traps.splice(trapIndex, 1);
}
} else if (o.type === 'bot') {
var botIndex = bots.indexOf(o);
if (botIndex > -1) {
bots.splice(botIndex, 1);
}
} else if (o.type === 'infectedBot') {
var ibotIndex = infectedBots.indexOf(o);
if (ibotIndex > -1) {
infectedBots.splice(ibotIndex, 1);
}
}
// Remove from worldObjects and destroy
o.destroy();
worldObjects.splice(i, 1);
return;
}
}
// Check if erasing Zin
if (zin && zin.intersectsPoint && zin.intersectsPoint(x, y)) {
zin.destroy();
zin = null;
return;
}
return;
}
// Check if touching an object to drag
for (var i = worldObjects.length - 1; i >= 0; i--) {
var o = worldObjects[i];
if (o.intersectsPoint(x, y)) {
draggingObj = o;
dragOffsetX = x - o.x;
dragOffsetY = y - o.y;
return;
}
}
// Place new object
placeObject(selectedTool, x, y);
} else {
// Play mode: move Zin
if (zin) {
// Check if target position would collide with blocks
var canMoveTo = true;
var tempX = zin.x;
var tempY = zin.y;
zin.x = x;
zin.y = y;
for (var i = 0; i < worldObjects.length; i++) {
if (worldObjects[i].type === 'block' && zin.intersects(worldObjects[i])) {
canMoveTo = false;
break;
}
}
zin.x = tempX;
zin.y = tempY;
if (canMoveTo) {
zin.targetX = x;
zin.targetY = y;
zin.speed = 12; // Reset to normal speed when manually controlled
zin.moving = true;
}
}
}
};
game.move = function (x, y, obj) {
if (buildMode && draggingObj) {
draggingObj.x = x - dragOffsetX;
draggingObj.y = y - dragOffsetY;
}
};
game.up = function (x, y, obj) {
draggingObj = null;
};
// --- Play Mode: Reset state, count chips, etc ---
function startPlayMode() {
collectedChips = 0;
totalChips = 0;
for (var i = 0; i < chips.length; i++) {
chips[i].collected = false;
chips[i].visible = true;
totalChips++;
}
chipTxt.setText('Chips: 0/' + totalChips);
// Reset traps
for (var i = 0; i < traps.length; i++) {
traps[i].armed = true;
traps[i].alpha = 1;
}
// Reset bots
for (var i = 0; i < bots.length; i++) {
bots[i].cured = false;
bots[i].visible = true;
}
// Reset infected bots
for (var i = 0; i < infectedBots.length; i++) {
infectedBots[i].visible = true;
}
// Place Zin at start if not present
if (!zin) {
zin = new Zin();
zin.x = 400;
zin.y = 400;
game.addChild(zin);
}
zin.moving = false;
zin.targetX = zin.x;
zin.targetY = zin.y;
}
// --- Main Game Loop ---
game.update = function () {
// Only update in play mode
if (!buildMode) {
// Update Zin
if (zin) {
zin.update();
// Randomly make Zin say good stuff (1 in 300 chance per frame)
if (Math.random() < 0.003333) {
zin.sayMessage();
}
}
// Update bots
for (var i = 0; i < bots.length; i++) {
bots[i].update();
}
for (var i = 0; i < infectedBots.length; i++) {
infectedBots[i].update();
}
// Zin collects chips
for (var i = 0; i < chips.length; i++) {
var chip = chips[i];
if (!chip.collected && zin && zin.intersects(chip)) {
chip.collected = true;
chip.visible = false;
collectedChips++;
chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips);
LK.getSound('chipCollect').play();
// Make Zin say something good
zin.sayMessage();
// Win if all chips collected
if (collectedChips >= totalChips && totalChips > 0) {
LK.showYouWin();
}
}
}
// Infected bots touching chips turn into normal bots
for (var i = 0; i < chips.length; i++) {
var chip = chips[i];
if (!chip.collected) {
for (var ib = infectedBots.length - 1; ib >= 0; ib--) {
var infectedBot = infectedBots[ib];
if (infectedBot.visible && infectedBot.intersects(chip)) {
// Convert infected bot to normal bot
var newBot = new Bot();
newBot.x = infectedBot.x;
newBot.y = infectedBot.y;
bots.push(newBot);
worldObjects.push(newBot);
game.addChild(newBot);
// Remove infected bot
infectedBot.destroy();
infectedBots.splice(ib, 1);
var objIndex = worldObjects.indexOf(infectedBot);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
// Collect the chip
chip.collected = true;
chip.visible = false;
collectedChips++;
chipTxt.setText('Chips: ' + collectedChips + '/' + totalChips);
LK.getSound('botCured').play();
// Win if all chips collected
if (collectedChips >= totalChips && totalChips > 0) {
LK.showYouWin();
}
break;
}
}
}
}
// Check if any robot (Zin, bots, or infected bots) triggers traps
for (var i = 0; i < traps.length; i++) {
var trap = traps[i];
var robotOnTrap = false;
// Check Zin
if (trap.armed && zin && zin.intersects(trap)) {
robotOnTrap = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// Check friendly bots
for (var b = 0; b < bots.length; b++) {
if (trap.armed && bots[b].visible && bots[b].intersects(trap)) {
robotOnTrap = true;
break;
}
}
// Check infected bots
for (var ib = 0; ib < infectedBots.length; ib++) {
if (trap.armed && infectedBots[ib].visible && infectedBots[ib].intersects(trap)) {
robotOnTrap = true;
break;
}
}
// Close trap if any robot is on it
if (robotOnTrap && trap.armed) {
trap.armed = false;
LK.effects.flashObject(trap, 0xff0000, 600);
trap.alpha = 0.3;
LK.getSound('trapTrigger').play();
// Kill any bots on the trap
for (var b = bots.length - 1; b >= 0; b--) {
if (bots[b].visible && bots[b].intersects(trap)) {
LK.effects.flashObject(bots[b], 0xff0000, 400);
bots[b].destroy();
bots.splice(b, 1);
// Remove from worldObjects array
var objIndex = worldObjects.indexOf(bots[b]);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
}
}
// Kill any infected bots on the trap
for (var ib = infectedBots.length - 1; ib >= 0; ib--) {
if (infectedBots[ib].visible && infectedBots[ib].intersects(trap)) {
LK.effects.flashObject(infectedBots[ib], 0xff0000, 400);
infectedBots[ib].destroy();
infectedBots.splice(ib, 1);
// Remove from worldObjects array
var objIndex = worldObjects.indexOf(infectedBots[ib]);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
}
}
}
}
// Normal bots get infected when touching infected bots
for (var b = bots.length - 1; b >= 0; b--) {
var normalBot = bots[b];
if (normalBot.visible && !normalBot.infected) {
for (var ib = 0; ib < infectedBots.length; ib++) {
var infectedBot = infectedBots[ib];
if (infectedBot.visible && normalBot.intersects(infectedBot)) {
// Convert normal bot to infected bot
var newInfectedBot = new InfectedBot();
newInfectedBot.x = normalBot.x;
newInfectedBot.y = normalBot.y;
infectedBots.push(newInfectedBot);
worldObjects.push(newInfectedBot);
game.addChild(newInfectedBot);
// Remove normal bot
normalBot.destroy();
bots.splice(b, 1);
var objIndex = worldObjects.indexOf(normalBot);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
LK.getSound('robot').play();
break;
}
}
}
}
// Zin cures infected bots by making them disappear
for (var i = infectedBots.length - 1; i >= 0; i--) {
var ibot = infectedBots[i];
if (ibot.visible && zin && zin.intersects(ibot)) {
// Remove infected bot completely
ibot.destroy();
infectedBots.splice(i, 1);
var objIndex = worldObjects.indexOf(ibot);
if (objIndex > -1) {
worldObjects.splice(objIndex, 1);
}
LK.getSound('botCured').play();
// Make Zin say something good
zin.sayMessage();
}
}
}
};
// --- Utility: intersectsPoint for drag selection ---
Container.prototype.intersectsPoint = function (x, y) {
// Simple bounding box check
var left = this.x - this.width / 2;
var right = this.x + this.width / 2;
var top = this.y - this.height / 2;
var bottom = this.y + this.height / 2;
return x >= left && x <= right && y >= top && y <= bottom;
};
robot. In-Game asset. 2d. High contrast. No shadows
computer chip. In-Game asset. 2d. High contrast. No shadows
glitch robot. In-Game asset. 2d. High contrast. No shadows
bear trap 1d. In-Game asset. 2d. High contrast. No shadows
block. In-Game asset. 2d. High contrast. No shadows
sign. In-Game asset. 2d. High contrast. No shadows
cute robot. In-Game asset. 2d. High contrast. No shadows