User prompt
fes que las alcantarillas nomes o pugui utilitzar el impostor
User prompt
posa las tasks
User prompt
quan las portes estan obertas es tenen que poder atravesar
User prompt
fes salas i pasadisos i utilitza las portes per fer las salas i posa las alcantarillas
User prompt
fes zoom a la camera ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
posa un temoritzador per matar ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'ReferenceError: showDoorButton is not defined' in or related to this line: 'showDoorButton(closestDoor);' Line Number: 4420
User prompt
fes un fantasma al matar a un tripulan ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'ReferenceError: showDoorButton is not defined' in or related to this line: 'showDoorButton(closestDoor);' Line Number: 4332
User prompt
el boto de matar te que apareixer quans trobes un tripulan
User prompt
emb va molt lagegat
User prompt
emb va molt lagegat
User prompt
arregla qualsevol error que trobis
User prompt
arregla qualsevol error que trobis
User prompt
fes unas alcantarillas que et pots teletransportar de una a l'altre
User prompt
a mi tambe donam 4 tasks
User prompt
fes 4 misions per cada tripulan
User prompt
fes que la ai pugui arreglar sabotages i reportar mors
User prompt
fes que no puguis mirar a traves de las murs
User prompt
en el mission panel haura les misions que tens que fer
User prompt
fes un apartat on apareixin las misions que tens que fer
User prompt
fes que cada tripulant te las sevas propias tasks
User prompt
fes la nau mes gran amb les seves salas
User prompt
fes un boto per cada sabotage electricitat,oxigen,portes de la nau,reactor i conexions
User prompt
Please fix the bug: 'TypeError: phaseText.getText is not a function' in or related to this line: 'var currentText = phaseText.getText() || '';' Line Number: 3141
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var CablesTask = Container.expand(function (x, y, id, assignedPlayerId) { var self = Container.call(this); self.taskId = id || 0; self.isCompleted = false; self.x = x; self.y = y; self.taskType = 'cables'; self.taskName = 'Fix Wiring'; self.taskDescription = 'Connect the colored wires'; self.isActive = false; self.wiresConnected = 0; self.totalWires = 4; self.wires = []; self.connectors = []; self.isNearPlayer = false; self.lastNearPlayer = false; self.assignedPlayerId = assignedPlayerId || -1; // Which player this task is assigned to (-1 = any player) // Task panel background var taskPanel = self.attachAsset('cablePanel', { anchorX: 0.5, anchorY: 0.5 }); taskPanel.alpha = 0.8; // Add task name text var taskNameText = new Text2(self.taskName, { size: 24, fill: 0xFFFFFF }); taskNameText.anchor.set(0.5, 0); taskNameText.y = -180; self.addChild(taskNameText); // Add interaction hint text var interactionText = new Text2('Click to start wiring', { size: 18, fill: 0x00FFFF }); interactionText.anchor.set(0.5, 0); interactionText.y = 170; interactionText.alpha = 0; self.addChild(interactionText); // Wire colors var wireColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00]; var leftConnectorPositions = []; var rightConnectorPositions = []; // Create left side connectors (sources) for (var i = 0; i < self.totalWires; i++) { var leftConnector = self.attachAsset('cableConnector', { anchorX: 0.5, anchorY: 0.5 }); leftConnector.x = -80; leftConnector.y = -60 + i * 40; leftConnector.tint = wireColors[i]; leftConnector.wireIndex = i; leftConnector.isSource = true; leftConnector.isConnected = false; self.connectors.push(leftConnector); leftConnectorPositions.push({ x: leftConnector.x, y: leftConnector.y }); } // Create right side connectors (destinations) - shuffled var shuffledColors = wireColors.slice(); for (var i = shuffledColors.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = shuffledColors[i]; shuffledColors[i] = shuffledColors[j]; shuffledColors[j] = temp; } for (var i = 0; i < self.totalWires; i++) { var rightConnector = self.attachAsset('cableConnector', { anchorX: 0.5, anchorY: 0.5 }); rightConnector.x = 80; rightConnector.y = -60 + i * 40; rightConnector.tint = shuffledColors[i]; rightConnector.wireIndex = wireColors.indexOf(shuffledColors[i]); rightConnector.isSource = false; rightConnector.isConnected = false; self.connectors.push(rightConnector); rightConnectorPositions.push({ x: rightConnector.x, y: rightConnector.y }); } // Create wires (initially disconnected) for (var i = 0; i < self.totalWires; i++) { var wire = self.attachAsset('cableWire', { anchorX: 0, anchorY: 0.5 }); wire.x = -70; wire.y = -60 + i * 40; wire.width = 60; wire.tint = wireColors[i]; wire.wireIndex = i; wire.isConnected = false; wire.alpha = 0.5; self.wires.push(wire); } self.startTask = function () { if (!self.isActive && !self.isCompleted) { self.isActive = true; // Show task panel with animation tween(taskPanel, { scaleX: 1.1, scaleY: 1.1, alpha: 1.0 }, { duration: 300, onFinish: function onFinish() { tween(taskPanel, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); // Show instruction var instructionText = new Text2('Connect matching wire colors', { size: 20, fill: 0xFFFF00 }); instructionText.anchor.set(0.5, 0); instructionText.y = -130; self.addChild(instructionText); // Remove after delay LK.setTimeout(function () { if (instructionText) { instructionText.destroy(); } }, 2000); } }; self.connectWire = function (sourceIndex, targetIndex) { if (sourceIndex === targetIndex && !self.wires[sourceIndex].isConnected) { // Correct connection self.wires[sourceIndex].isConnected = true; self.wires[sourceIndex].alpha = 1.0; self.wires[sourceIndex].width = 160; self.wiresConnected++; // Mark connectors as connected self.connectors.forEach(function (connector) { if (connector.wireIndex === sourceIndex) { connector.isConnected = true; // Flash connected effect tween(connector, { scaleX: 1.3, scaleY: 1.3 }, { duration: 150, onFinish: function onFinish() { tween(connector, { scaleX: 1.0, scaleY: 1.0 }, { duration: 150 }); } }); } }); // Play success sound LK.getSound('taskComplete').play(); // Check if all wires are connected if (self.wiresConnected >= self.totalWires) { self.complete(); } return true; } else { // Wrong connection - flash red var wrongWire = self.wires[sourceIndex]; tween(wrongWire, { tint: 0xff0000, alpha: 0.8 }, { duration: 200, onFinish: function onFinish() { tween(wrongWire, { tint: wireColors[sourceIndex], alpha: 0.5 }, { duration: 200 }); } }); return false; } }; self.complete = function () { if (!self.isCompleted) { self.isCompleted = true; self.isActive = false; // Hide task panel tween(taskPanel, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500 }); // Show completion text var completedText = new Text2('WIRING COMPLETE', { size: 24, fill: 0x00FF00 }); completedText.anchor.set(0.5, 0.5); completedText.y = 0; self.addChild(completedText); // Flash completion effect tween(completedText, { scaleX: 1.2, scaleY: 1.2, alpha: 1.0 }, { duration: 300, onFinish: function onFinish() { tween(completedText, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 300 }); } }); return true; } return false; }; self.update = function () { if (self.isCompleted) return; // Check if player is near self.lastNearPlayer = self.isNearPlayer; self.isNearPlayer = false; if (currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); // Only show task if it's assigned to current player or unassigned var isAssignedToCurrentPlayer = self.assignedPlayerId === -1 || self.assignedPlayerId === currentPlayer.playerId; self.isNearPlayer = distance < 120 && isAssignedToCurrentPlayer; } // Show interaction hint when player gets close if (!self.lastNearPlayer && self.isNearPlayer) { interactionText.alpha = 1; tween(interactionText, { y: 180, alpha: 0.9 }, { duration: 300 }); } // Hide interaction hint when player moves away if (self.lastNearPlayer && !self.isNearPlayer) { tween(interactionText, { y: 170, alpha: 0 }, { duration: 300 }); self.isActive = false; } }; self.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); // Only allow interaction if task is assigned to current player or unassigned var isAssignedToCurrentPlayer = self.assignedPlayerId === -1 || self.assignedPlayerId === currentPlayer.playerId; if (distance < 120 && !self.isCompleted && isAssignedToCurrentPlayer) { if (!self.isActive) { self.startTask(); } else { // Handle wire connection clicks var localX = x - self.x; var localY = y - self.y; // Check if clicking on a connector for (var i = 0; i < self.connectors.length; i++) { var connector = self.connectors[i]; var connectorDistance = Math.sqrt(Math.pow(localX - connector.x, 2) + Math.pow(localY - connector.y, 2)); if (connectorDistance < 25 && connector.isSource && !connector.isConnected) { // Find matching target connector for (var j = 0; j < self.connectors.length; j++) { var targetConnector = self.connectors[j]; if (!targetConnector.isSource && targetConnector.wireIndex === connector.wireIndex) { self.connectWire(connector.wireIndex, targetConnector.wireIndex); break; } } break; } } } } } }; return self; }); var Camera = Container.expand(function (x, y, id, viewAngle) { var self = Container.call(this); self.cameraId = id || 0; self.x = x; self.y = y; self.viewAngle = viewAngle || 0; self.detectionRange = 200; self.isActive = true; self.playersInView = []; // Camera graphics var cameraGraphics = self.attachAsset('camera', { anchorX: 0.5, anchorY: 0.5 }); cameraGraphics.rotation = self.viewAngle; // Camera name text var cameraNameText = new Text2('CAM ' + (self.cameraId + 1), { size: 16, fill: 0xFFFFFF }); cameraNameText.anchor.set(0.5, 0); cameraNameText.y = -30; self.addChild(cameraNameText); // Detection light indicator var detectionLight = self.attachAsset('cableConnector', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); detectionLight.x = 0; detectionLight.y = 25; detectionLight.tint = 0x00FF00; self.update = function () { if (!self.isActive) return; // Clear previous detections self.playersInView = []; // Check for players in camera view if (players && players.length > 0) { for (var i = 0; i < players.length; i++) { var player = players[i]; if (!player.isAlive) continue; var distance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2)); if (distance <= self.detectionRange) { // Check if player is within camera's field of view var angleToPlayer = Math.atan2(player.y - self.y, player.x - self.x); var angleDiff = Math.abs(angleToPlayer - self.viewAngle); // Normalize angle difference if (angleDiff > Math.PI) { angleDiff = 2 * Math.PI - angleDiff; } // Field of view is 90 degrees (π/2 radians) if (angleDiff <= Math.PI / 4) { self.playersInView.push(player); } } } } // Update detection light based on players in view if (self.playersInView.length > 0) { detectionLight.tint = 0xFF0000; // Red when detecting players if (LK.ticks % 30 < 15) { detectionLight.alpha = 1.0; } else { detectionLight.alpha = 0.5; } } else { detectionLight.tint = 0x00FF00; // Green when no detection detectionLight.alpha = 1.0; } }; return self; }); var Door = Container.expand(function (x, y, id, orientation) { var self = Container.call(this); self.doorId = id || 0; self.x = x; self.y = y; self.orientation = orientation || 0; // 0 = horizontal, 1 = vertical self.isOpen = true; self.closeTimer = 0; self.closeDuration = 10000; // 10 seconds self.isNearPlayer = false; self.lastNearPlayer = false; // Door graphics var doorGraphics = self.attachAsset('door', { anchorX: 0.5, anchorY: 0.5 }); if (self.orientation === 1) { doorGraphics.rotation = Math.PI / 2; } // Door status indicator var statusText = new Text2('OPEN', { size: 14, fill: 0x00FF00 }); statusText.anchor.set(0.5, 0); statusText.y = -40; self.addChild(statusText); // Visual indicator for impostors var impostorIndicator = self.attachAsset('doorButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); impostorIndicator.y = 20; impostorIndicator.alpha = 0; impostorIndicator.tint = 0xFF0000; self.close = function () { if (self.isOpen) { self.isOpen = false; self.closeTimer = self.closeDuration; // Change graphics doorGraphics.destroy(); doorGraphics = self.attachAsset('doorClosed', { anchorX: 0.5, anchorY: 0.5 }); if (self.orientation === 1) { doorGraphics.rotation = Math.PI / 2; } // Update status statusText.setText('CLOSED'); statusText.tint = 0xFF0000; // Play sound LK.getSound('doorClose').play(); // Flash effect tween(doorGraphics, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(doorGraphics, { tint: 0xFFFFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } }; self.open = function () { if (!self.isOpen) { self.isOpen = true; self.closeTimer = 0; // Change graphics doorGraphics.destroy(); doorGraphics = self.attachAsset('door', { anchorX: 0.5, anchorY: 0.5 }); if (self.orientation === 1) { doorGraphics.rotation = Math.PI / 2; } // Update status statusText.setText('OPEN'); statusText.tint = 0x00FF00; // Play sound LK.getSound('doorOpen').play(); // Flash effect tween(doorGraphics, { tint: 0x00FF00, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(doorGraphics, { tint: 0xFFFFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } }; self.update = function () { // Auto-open after timer expires if (!self.isOpen && self.closeTimer > 0) { self.closeTimer -= 16.67; // Roughly 1 frame at 60fps if (self.closeTimer <= 0) { self.open(); } } // Check if impostor player is near self.lastNearPlayer = self.isNearPlayer; self.isNearPlayer = false; if (currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); self.isNearPlayer = distance < 100; } // Show impostor indicator when impostor is near if (currentPlayer && currentPlayer.isImpostor && self.isNearPlayer) { impostorIndicator.alpha = 0.8; if (LK.ticks % 30 < 15) { impostorIndicator.alpha = 1.0; } else { impostorIndicator.alpha = 0.6; } } else { impostorIndicator.alpha = 0; } // Update timer display if (!self.isOpen && self.closeTimer > 0) { var secondsLeft = Math.ceil(self.closeTimer / 1000); statusText.setText('CLOSED (' + secondsLeft + 's)'); } }; self.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); if (distance < 100) { if (self.isOpen) { self.close(); } else { self.open(); } } } }; // Check if door blocks movement self.blocksMovement = function (x, y, width, height) { if (self.isOpen) return false; var playerLeft = x - width / 2; var playerRight = x + width / 2; var playerTop = y - height / 2; var playerBottom = y + height / 2; var doorLeft = self.x - 20; var doorRight = self.x + 20; var doorTop = self.y - 60; var doorBottom = self.y + 60; if (self.orientation === 1) { // Vertical door doorLeft = self.x - 60; doorRight = self.x + 60; doorTop = self.y - 20; doorBottom = self.y + 20; } // Check if player rectangle intersects with door rectangle return playerLeft < doorRight && playerRight > doorLeft && playerTop < doorBottom && playerBottom > doorTop; }; return self; }); var Player = Container.expand(function (isImpostor, color, id) { var self = Container.call(this); self.isImpostor = isImpostor || false; self.playerColor = color || 0x00ff00; self.playerId = id || 0; self.isAlive = true; self.speed = 3; self.targetX = 0; self.targetY = 0; self.tasksCompleted = 0; self.maxTasks = 5; self.canVote = true; self.lastKillTime = 0; self.killCooldown = 30000; // 30 seconds self.currentTask = null; // Track current task being worked on self.taskCompletionTimer = 0; // Timer for task completion self.assignedTasks = []; // Individual tasks assigned to this player var playerGraphics = self.attachAsset(self.isImpostor ? 'impostor' : 'crewmate', { anchorX: 0.5, anchorY: 0.5 }); if (!self.isImpostor) { playerGraphics.tint = self.playerColor; } self.moveTo = function (x, y) { var playerSize = 80; // Player width/height // Only set target if destination is not inside a wall if (!checkWallCollision(x, y, playerSize, playerSize)) { self.targetX = x; self.targetY = y; } else { // Keep current position as target if new position would collide self.targetX = self.x; self.targetY = self.y; } }; self.eliminate = function () { if (self.isAlive) { self.isAlive = false; self.alpha = 0.3; LK.getSound('eliminate').play(); } }; self.completeTask = function () { if (!self.isImpostor && self.isAlive) { self.tasksCompleted++; LK.getSound('taskComplete').play(); return true; } return false; }; self.canKill = function () { return self.isImpostor && self.isAlive && LK.ticks - self.lastKillTime > self.killCooldown; }; self.kill = function (target) { if (self.canKill() && target.isAlive && !target.isImpostor) { target.eliminate(); self.lastKillTime = LK.ticks; return true; } return false; }; self.update = function () { if (!self.isAlive) return; // Move towards target var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { var newX = self.x + dx / distance * self.speed; var newY = self.y + dy / distance * self.speed; var playerSize = 80; // Player width/height // Check collision before moving if (!checkWallCollision(newX, newY, playerSize, playerSize)) { self.x = newX; self.y = newY; } else { // Try moving only on X axis if (!checkWallCollision(newX, self.y, playerSize, playerSize)) { self.x = newX; } else if (!checkWallCollision(self.x, newY, playerSize, playerSize)) { // Try moving only on Y axis self.y = newY; } } } }; return self; }); var SecurityRoom = Container.expand(function (x, y) { var self = Container.call(this); self.x = x; self.y = y; self.isActive = false; self.currentCameraIndex = 0; self.isNearPlayer = false; self.lastNearPlayer = false; // Main security console var console = self.attachAsset('cameraFrame', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.0 }); console.tint = 0x222222; // Monitor screens (3x2 grid) self.monitors = []; var monitorPositions = [{ x: -150, y: -80 }, { x: 0, y: -80 }, { x: 150, y: -80 }, { x: -150, y: 80 }, { x: 0, y: 80 }, { x: 150, y: 80 }]; for (var i = 0; i < 6; i++) { var monitorFrame = self.attachAsset('cameraFrame', { anchorX: 0.5, anchorY: 0.5 }); monitorFrame.x = monitorPositions[i].x; monitorFrame.y = monitorPositions[i].y; monitorFrame.tint = 0x333333; var monitorScreen = self.attachAsset('cameraView', { anchorX: 0.5, anchorY: 0.5 }); monitorScreen.x = monitorPositions[i].x; monitorScreen.y = monitorPositions[i].y; // Camera label var cameraLabel = new Text2('CAM ' + (i + 1), { size: 18, fill: 0x00FFFF }); cameraLabel.anchor.set(0.5, 0); cameraLabel.x = monitorPositions[i].x; cameraLabel.y = monitorPositions[i].y - 75; self.addChild(cameraLabel); // Player count display var playerCountDisplay = new Text2('0 PLAYERS', { size: 14, fill: 0xFFFFFF }); playerCountDisplay.anchor.set(0.5, 0.5); playerCountDisplay.x = monitorPositions[i].x; playerCountDisplay.y = monitorPositions[i].y; self.addChild(playerCountDisplay); self.monitors.push({ frame: monitorFrame, screen: monitorScreen, label: cameraLabel, playerCount: playerCountDisplay }); } // Security room title var titleText = new Text2('SECURITY ROOM', { size: 32, fill: 0x00FFFF }); titleText.anchor.set(0.5, 0); titleText.y = -200; self.addChild(titleText); // Interaction hint var interactionText = new Text2('View Security Cameras', { size: 20, fill: 0x00FFFF }); interactionText.anchor.set(0.5, 0); interactionText.y = 200; interactionText.alpha = 0; self.addChild(interactionText); // Instructions when active var instructionText = new Text2('Click monitors to switch cameras', { size: 16, fill: 0xFFFF00 }); instructionText.anchor.set(0.5, 0); instructionText.y = 220; instructionText.alpha = 0; self.addChild(instructionText); self.activate = function () { if (!self.isActive) { self.isActive = true; // Show activation animation tween(console, { scaleX: 2.7, scaleY: 2.2, tint: 0x004400 }, { duration: 300, onFinish: function onFinish() { tween(console, { scaleX: 2.5, scaleY: 2.0, tint: 0x222222 }, { duration: 200 }); } }); // Show instructions instructionText.alpha = 1; } }; self.deactivate = function () { if (self.isActive) { self.isActive = false; instructionText.alpha = 0; // Reset all monitors for (var i = 0; i < self.monitors.length; i++) { self.monitors[i].screen.tint = 0x000000; self.monitors[i].frame.tint = 0x333333; } } }; self.update = function () { // Check if player is near self.lastNearPlayer = self.isNearPlayer; self.isNearPlayer = false; if (currentPlayer && currentPlayer.isAlive) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); self.isNearPlayer = distance < 150; } // Show interaction hint when player gets close if (!self.lastNearPlayer && self.isNearPlayer) { interactionText.alpha = 1; tween(interactionText, { y: 210, alpha: 0.8 }, { duration: 300 }); } // Hide interaction hint when player moves away if (self.lastNearPlayer && !self.isNearPlayer) { tween(interactionText, { y: 200, alpha: 0 }, { duration: 300 }); self.deactivate(); } // Update camera feeds if active if (self.isActive && cameras && cameras.length > 0) { for (var i = 0; i < Math.min(self.monitors.length, cameras.length); i++) { var camera = cameras[i]; var monitor = self.monitors[i]; if (camera && monitor) { // Update player count display var playerCount = camera.playersInView.length; monitor.playerCount.setText(playerCount + ' PLAYERS'); // Update monitor colors based on activity if (playerCount > 0) { // Red tint when players detected monitor.screen.tint = 0x440000; monitor.frame.tint = 0xFF0000; // Flash effect for active cameras if (LK.ticks % 60 < 30) { monitor.frame.alpha = 1.0; } else { monitor.frame.alpha = 0.7; } } else { // Green tint when no players monitor.screen.tint = 0x004400; monitor.frame.tint = 0x333333; monitor.frame.alpha = 1.0; } } } } }; self.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isAlive) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); if (distance < 150) { if (!self.isActive) { self.activate(); } else { // Check if clicking on a specific monitor var localX = x - self.x; var localY = y - self.y; for (var i = 0; i < self.monitors.length; i++) { var monitor = self.monitors[i]; var monitorX = monitor.frame.x; var monitorY = monitor.frame.y; var monitorDistance = Math.sqrt(Math.pow(localX - monitorX, 2) + Math.pow(localY - monitorY, 2)); if (monitorDistance < 75) { // Highlight selected monitor monitor.frame.tint = 0x0000FF; // Reset other monitors for (var j = 0; j < self.monitors.length; j++) { if (j !== i) { var otherMonitor = self.monitors[j]; if (cameras[j] && cameras[j].playersInView.length > 0) { otherMonitor.frame.tint = 0xFF0000; } else { otherMonitor.frame.tint = 0x333333; } } } break; } } } } } }; return self; }); var Task = Container.expand(function (x, y, id, taskType, assignedPlayerId) { var self = Container.call(this); self.taskId = id || 0; self.isCompleted = false; self.x = x; self.y = y; self.taskType = taskType || 'basic'; self.completionProgress = 0; self.requiredClicks = 1; self.completionTime = 0; self.isBeingCompleted = false; self.interactionTimer = 0; self.taskStarted = false; self.isNearPlayer = false; self.lastNearPlayer = false; self.assignedPlayerId = assignedPlayerId || -1; // Which player this task is assigned to (-1 = any player) // Set task properties based on type switch (self.taskType) { case 'electrical': self.requiredClicks = 3; self.assetName = 'electricalTask'; self.taskName = 'Fix Wiring'; self.taskDescription = 'Connect the wires in the correct order'; break; case 'engine': self.requiredClicks = 5; self.assetName = 'engineTask'; self.taskName = 'Fuel Engines'; self.taskDescription = 'Fill the fuel tanks'; break; case 'reactor': self.requiredClicks = 7; self.assetName = 'reactorTask'; self.taskName = 'Stabilize Reactor'; self.taskDescription = 'Balance the reactor core'; break; case 'navigation': self.requiredClicks = 4; self.assetName = 'navigationTask'; self.taskName = 'Chart Course'; self.taskDescription = 'Set navigation coordinates'; break; case 'medical': self.requiredClicks = 6; self.assetName = 'medicalTask'; self.taskName = 'Submit Scan'; self.taskDescription = 'Complete medical scan'; break; case 'cables': self.requiredClicks = 4; self.assetName = 'electricalTask'; self.taskName = 'Fix Wiring'; self.taskDescription = 'Connect the colored wires'; break; default: self.requiredClicks = 1; self.assetName = 'task'; self.taskName = 'Basic Task'; self.taskDescription = 'Complete basic task'; break; } var taskGraphics = self.attachAsset(self.assetName, { anchorX: 0.5, anchorY: 0.5 }); // Add task name text var taskNameText = new Text2(self.taskName, { size: 24, fill: 0xFFFFFF }); taskNameText.anchor.set(0.5, 0); taskNameText.y = -50; self.addChild(taskNameText); // Add interaction hint text (initially hidden) var interactionText = new Text2('Hold to complete', { size: 20, fill: 0x00FFFF }); interactionText.anchor.set(0.5, 0); interactionText.y = 50; interactionText.alpha = 0; self.addChild(interactionText); self.complete = function () { if (!self.isCompleted) { self.isCompleted = true; taskGraphics.destroy(); taskNameText.destroy(); if (interactionText) { interactionText.destroy(); } if (self.lastProgressText) { self.lastProgressText.destroy(); } var completedGraphics = self.attachAsset('taskCompleted', { anchorX: 0.5, anchorY: 0.5 }); var completedText = new Text2('Complete', { size: 20, fill: 0x00FF00 }); completedText.anchor.set(0.5, 0); completedText.y = -40; self.addChild(completedText); return true; } return false; }; self.startTask = function () { if (!self.taskStarted && !self.isCompleted) { self.taskStarted = true; self.interactionTimer = 0; // Visual feedback for task start tween(taskGraphics, { scaleX: 1.2, scaleY: 1.2, tint: 0x00FFFF }, { duration: 200, onFinish: function onFinish() { tween(taskGraphics, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 200 }); } }); // Show progress for multi-step tasks if (self.requiredClicks > 1) { var progressText = new Text2(self.completionProgress + '/' + self.requiredClicks, { size: 32, fill: 0xFFFF00 }); progressText.anchor.set(0.5, 0); progressText.y = 70; self.addChild(progressText); // Remove old progress text if (self.lastProgressText) { self.lastProgressText.destroy(); } self.lastProgressText = progressText; } } }; self.update = function () { if (self.isCompleted) return; // Check if player is near self.lastNearPlayer = self.isNearPlayer; self.isNearPlayer = false; if (currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); // Only show task if it's assigned to current player or unassigned var isAssignedToCurrentPlayer = self.assignedPlayerId === -1 || self.assignedPlayerId === currentPlayer.playerId; self.isNearPlayer = distance < 100 && isAssignedToCurrentPlayer; } // Show interaction hint when player gets close if (!self.lastNearPlayer && self.isNearPlayer) { interactionText.alpha = 1; tween(interactionText, { y: 60, alpha: 0.8 }, { duration: 300 }); } // Hide interaction hint when player moves away if (self.lastNearPlayer && !self.isNearPlayer) { tween(interactionText, { y: 50, alpha: 0 }, { duration: 300 }); self.taskStarted = false; self.interactionTimer = 0; } // Update interaction timer if task is being worked on if (self.taskStarted && self.isNearPlayer) { self.interactionTimer += 16.67; // Roughly 1 frame at 60fps // Visual progress indicator var progressPercent = Math.min(self.interactionTimer / 2000, 1.0); // 2 seconds per step if (progressPercent < 1.0) { taskGraphics.alpha = 0.5 + progressPercent * 0.5; } else { // Complete this step self.completionProgress++; self.interactionTimer = 0; taskGraphics.alpha = 1.0; // Flash effect for progress tween(taskGraphics, { alpha: 0.3, tint: 0x00FF00 }, { duration: 150, onFinish: function onFinish() { tween(taskGraphics, { alpha: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); // Update progress text if (self.requiredClicks > 1 && self.lastProgressText) { self.lastProgressText.setText(self.completionProgress + '/' + self.requiredClicks); } // Check if task is complete if (self.completionProgress >= self.requiredClicks) { self.complete(); if (currentPlayer) { currentPlayer.completeTask(); } completedTasks++; updateTaskProgress(); } else { // Reset for next step self.taskStarted = false; } } } }; self.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor) { var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2)); // Only allow interaction if task is assigned to current player or unassigned var isAssignedToCurrentPlayer = self.assignedPlayerId === -1 || self.assignedPlayerId === currentPlayer.playerId; if (distance < 100 && !self.isCompleted && isAssignedToCurrentPlayer) { self.startTask(); } } }; return self; }); var VoteButton = Container.expand(function (playerId, x, y) { var self = Container.call(this); self.playerId = playerId; self.x = x; self.y = y; self.votes = 0; var buttonGraphics = self.attachAsset('voteButton', { anchorX: 0.5, anchorY: 0.5 }); var voteText = new Text2('Vote Player ' + playerId, { size: 40, fill: 0xFFFFFF }); voteText.anchor.set(0.5, 0.5); self.addChild(voteText); self.down = function (x, y, obj) { if (votingPhase && currentPlayer && currentPlayer.canVote) { self.votes++; currentPlayer.canVote = false; LK.getSound('vote').play(); voteText.setText('Votes: ' + self.votes); // Check if voting is complete var alivePlayers = players.filter(function (p) { return p.isAlive; }); var totalVotes = voteButtons.reduce(function (sum, button) { return sum + button.votes; }, 0); if (totalVotes >= alivePlayers.length) { endVoting(); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ var players = []; var tasks = []; var currentPlayer = null; var totalTasks = 15; var completedTasks = 0; var impostorCount = 2; var votingPhase = false; var voteButtons = []; var gamePhase = 'roleSelection'; // 'roleSelection', 'playing', 'voting', 'gameOver' var meetingCooldown = 0; var meetingCooldownTime = 15000; // 15 seconds var roleSelectionComplete = false; var killButton = null; var killTarget = null; var sabotageButton = null; var sabotageTarget = null; var sabotageElectricityButton = null; var sabotageOxygenButton = null; var sabotageDoorsButton = null; var sabotageReactorButton = null; var sabotageConnectionsButton = null; var reportButton = null; var reportTarget = null; var taskButton = null; var currentTaskTarget = null; var cameraButton = null; // UI Elements var taskProgressText = new Text2('Tasks: 0/' + totalTasks, { size: 60, fill: 0xFFFFFF }); taskProgressText.anchor.set(0.5, 0); LK.gui.top.addChild(taskProgressText); // Task completion UI var taskCompletionText = new Text2('', { size: 40, fill: 0x00FF00 }); taskCompletionText.anchor.set(0.5, 0.5); taskCompletionText.alpha = 0; LK.gui.center.addChild(taskCompletionText); // Current task indicator var currentTaskText = new Text2('', { size: 30, fill: 0xFFFF00 }); currentTaskText.anchor.set(0.5, 1); currentTaskText.y = -50; currentTaskText.alpha = 0; LK.gui.center.addChild(currentTaskText); // Mission panel UI var missionPanel = LK.getAsset('cameraFrame', { anchorX: 0, anchorY: 0, scaleX: 2.5, scaleY: 3.0, x: 50, y: 200 }); missionPanel.tint = 0x222222; missionPanel.alpha = 0.8; missionPanel.visible = false; LK.gui.topLeft.addChild(missionPanel); var missionPanelTitle = new Text2('MISSIONS', { size: 32, fill: 0x00FFFF }); missionPanelTitle.anchor.set(0.5, 0); missionPanelTitle.x = 250; missionPanelTitle.y = 220; missionPanelTitle.visible = false; LK.gui.topLeft.addChild(missionPanelTitle); var missionTaskTexts = []; var missionToggleButton = LK.getAsset('taskButton', { anchorX: 0, anchorY: 0, scaleX: 0.6, scaleY: 0.6, x: 120, y: 200 }); missionToggleButton.tint = 0x00FFFF; missionToggleButton.alpha = 0; missionToggleButton.visible = false; LK.gui.topLeft.addChild(missionToggleButton); var missionToggleText = new Text2('MISSIONS', { size: 20, fill: 0xFFFFFF }); missionToggleText.anchor.set(0.5, 0.5); missionToggleText.x = 36; missionToggleText.y = 36; missionToggleButton.addChild(missionToggleText); var missionPanelVisible = false; // Mission panel toggle handler missionToggleButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor && gamePhase === 'playing') { toggleMissionPanel(); } }; // Camera system variables var cameras = []; var securityRoom = null; // Door system variables var doors = []; var doorButton = null; var currentDoorTarget = null; // Sabotage system variables var activeSabotage = null; var sabotageTimer = 0; var sabotageEffect = null; var sabotageIndicators = []; var isSabotageActive = false; var sabotageTypes = ['electricity', 'oxygen', 'reactor', 'doors', 'communications']; // Progress Bar Elements var progressBarBackground = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 0.5, x: 0, y: 80 }); progressBarBackground.tint = 0x333333; LK.gui.top.addChild(progressBarBackground); var progressBarFill = LK.getAsset('wall', { anchorX: 0, anchorY: 0.5, scaleX: 0, scaleY: 0.4, x: -800, y: 80 }); progressBarFill.tint = 0x00ff00; LK.gui.top.addChild(progressBarFill); // Sabotage effect overlay (initially hidden) sabotageEffect = LK.getAsset('sabotageEffect', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); sabotageEffect.alpha = 0; sabotageEffect.visible = false; game.addChild(sabotageEffect); var phaseText = new Text2('Complete Tasks or Find Impostors!', { size: 50, fill: 0xFFFF00 }); phaseText.anchor.set(0.5, 0); phaseText.y = 100; LK.gui.top.addChild(phaseText); var playerCountText = new Text2('', { size: 40, fill: 0xFFFFFF }); playerCountText.anchor.set(0, 0); LK.gui.topRight.addChild(playerCountText); // Role selection UI var roleSelectionTitle = new Text2('Choose Your Role', { size: 80, fill: 0xFFFFFF }); roleSelectionTitle.anchor.set(0.5, 0.5); roleSelectionTitle.x = 1024; roleSelectionTitle.y = 800; game.addChild(roleSelectionTitle); var crewButton = LK.getAsset('voteButton', { anchorX: 0.5, anchorY: 0.5, x: 700, y: 1200, scaleX: 1.5, scaleY: 1.5 }); crewButton.tint = 0x00ff00; game.addChild(crewButton); var crewButtonText = new Text2('CREW MEMBER', { size: 50, fill: 0xFFFFFF }); crewButtonText.anchor.set(0.5, 0.5); crewButtonText.x = 700; crewButtonText.y = 1200; game.addChild(crewButtonText); var impostorButton = LK.getAsset('voteButton', { anchorX: 0.5, anchorY: 0.5, x: 1348, y: 1200, scaleX: 1.5, scaleY: 1.5 }); impostorButton.tint = 0xff0000; game.addChild(impostorButton); var impostorButtonText = new Text2('IMPOSTOR', { size: 50, fill: 0xFFFFFF }); impostorButtonText.anchor.set(0.5, 0.5); impostorButtonText.x = 1348; impostorButtonText.y = 1200; game.addChild(impostorButtonText); // Role selection instructions var instructionText = new Text2('Choose your role and start the game!', { size: 40, fill: 0xFFFF00 }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 1400; game.addChild(instructionText); // Create spaceship walls var walls = []; function createWalls() { // Outer perimeter walls - Extended ship dimensions var topWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 14, x: 1024, y: 150 })); walls.push(topWall); var bottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 14, x: 1024, y: 2600 })); walls.push(bottomWall); var leftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 12, x: 150, y: 1366 })); walls.push(leftWall); var rightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 12, x: 1900, y: 1366 })); walls.push(rightWall); // Upper corridor walls var upperLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 550, y: 600 })); walls.push(upperLeftWall); var upperRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 1500, y: 600 })); walls.push(upperRightWall); // Central corridor walls var centralTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, x: 1024, y: 1000 })); walls.push(centralTopWall); var centralBottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, x: 1024, y: 1700 })); walls.push(centralBottomWall); // Room dividers var leftRoomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 3, x: 700, y: 1000 })); walls.push(leftRoomWall); var rightRoomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 3, x: 1350, y: 1000 })); walls.push(rightRoomWall); // Lower corridor walls var lowerLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 550, y: 2100 })); walls.push(lowerLeftWall); var lowerRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 1500, y: 2100 })); walls.push(lowerRightWall); // Engine room walls var engineLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2, x: 400, y: 1800 })); walls.push(engineLeftWall); var engineRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2, x: 1650, y: 1800 })); walls.push(engineRightWall); // Security room walls - expanded for camera monitoring var securityWallTop = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.0, x: 1024, y: 450 })); walls.push(securityWallTop); var securityWallLeft = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2.0, x: 750, y: 600 })); walls.push(securityWallLeft); var securityWallRight = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2.0, x: 1300, y: 600 })); walls.push(securityWallRight); // Medical bay walls var medicalWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 1.5, x: 1200, y: 1500 })); walls.push(medicalWall); // Reactor room walls var reactorWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 1.5, x: 850, y: 1500 })); walls.push(reactorWall); // Command Bridge - New room at top var bridgeTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, x: 1024, y: 250 })); walls.push(bridgeTopWall); var bridgeLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 1.5, x: 700, y: 350 })); walls.push(bridgeLeftWall); var bridgeRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 1.5, x: 1350, y: 350 })); walls.push(bridgeRightWall); // Cargo Bay - New large room at bottom var cargoTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, x: 1024, y: 2200 })); walls.push(cargoTopWall); var cargoLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2, x: 500, y: 2350 })); walls.push(cargoLeftWall); var cargoRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2, x: 1550, y: 2350 })); walls.push(cargoRightWall); // Extended Laboratory - Left side expansion var labTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 300, y: 800 })); walls.push(labTopWall); var labBottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 300, y: 1200 })); walls.push(labBottomWall); var labRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2, x: 450, y: 1000 })); walls.push(labRightWall); // Extended Weapons Room - Right side expansion var weaponsTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 1750, y: 800 })); walls.push(weaponsTopWall); var weaponsBottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, x: 1750, y: 1200 })); walls.push(weaponsBottomWall); var weaponsLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 2, x: 1600, y: 1000 })); walls.push(weaponsLeftWall); // Communication Array - New room top right var commTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, x: 1600, y: 400 })); walls.push(commTopWall); var commBottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, x: 1600, y: 650 })); walls.push(commBottomWall); var commLeftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 1.5, x: 1400, y: 525 })); walls.push(commLeftWall); // Maintenance Tunnels - New room bottom left var maintTopWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, x: 400, y: 1800 })); walls.push(maintTopWall); var maintBottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, x: 400, y: 2050 })); walls.push(maintBottomWall); var maintRightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleY: 1.5, x: 600, y: 1925 })); walls.push(maintRightWall); // Add spaceship floor tiles - Extended coverage var _loop = function _loop() { for (floorY = 200; floorY < 2550; floorY += 100) { floorTile = game.addChild(LK.getAsset('shipFloor', { anchorX: 0.5, anchorY: 0.5, x: floorX, y: floorY })); // Send floor tiles to back game.setChildIndex(floorTile, 0); } function showDoorButton(target) { if (doorButton && currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor) { currentDoorTarget = target; doorButton.alpha = 1; doorButton.visible = true; // Update button text based on door state if (target.isOpen) { doorButtonText.setText('CLOSE DOOR'); } else { doorButtonText.setText('OPEN DOOR'); } // Flash effect to draw attention tween(doorButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(doorButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } } function hideDoorButton() { if (doorButton) { currentDoorTarget = null; doorButton.alpha = 0; doorButton.visible = false; } } doorButton.down = function (x, y, obj) { if (currentDoorTarget && currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor && gamePhase === 'playing') { if (currentDoorTarget.isOpen) { currentDoorTarget.close(); } else { currentDoorTarget.open(); } } }; }, floorY, floorTile; for (var floorX = 200; floorX < 1900; floorX += 100) { _loop(); } // Add main corridor highlights - central hub var mainCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 4, x: 1024, y: 1366 })); game.setChildIndex(mainCorridor, 1); // Add upper corridor - connects upper rooms var upperCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, scaleY: 2, x: 1024, y: 700 })); game.setChildIndex(upperCorridor, 1); // Add lower corridor - connects lower rooms var lowerCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, scaleY: 2, x: 1024, y: 2000 })); game.setChildIndex(lowerCorridor, 1); // Add left vertical corridor - connects left rooms var leftVerticalCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 8, x: 550, y: 1366 })); game.setChildIndex(leftVerticalCorridor, 1); // Add right vertical corridor - connects right rooms var rightVerticalCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 8, x: 1500, y: 1366 })); game.setChildIndex(rightVerticalCorridor, 1); // Add Command Bridge corridor var bridgeCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 1.5, x: 1024, y: 350 })); game.setChildIndex(bridgeCorridor, 1); // Add Cargo Bay corridor var cargoCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, scaleY: 2, x: 1024, y: 2350 })); game.setChildIndex(cargoCorridor, 1); // Add Laboratory corridor var labCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2, x: 300, y: 1000 })); game.setChildIndex(labCorridor, 1); // Add Weapons Room corridor var weaponsCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2, x: 1750, y: 1000 })); game.setChildIndex(weaponsCorridor, 1); // Add Communication Array corridor var commCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.5, x: 1600, y: 525 })); game.setChildIndex(commCorridor, 1); // Add Maintenance Tunnels corridor var maintCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.5, x: 400, y: 1925 })); game.setChildIndex(maintCorridor, 1); // Extended connecting corridors var bridgeConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 3, x: 1024, y: 500 })); game.setChildIndex(bridgeConnector, 1); var cargoConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, x: 1024, y: 2150 })); game.setChildIndex(cargoConnector, 1); // Side wing corridors var leftWingCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 10, x: 250, y: 1366 })); game.setChildIndex(leftWingCorridor, 1); var rightWingCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 10, x: 1800, y: 1366 })); game.setChildIndex(rightWingCorridor, 1); // Add connecting corridors between upper and main areas var upperLeftConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 3, x: 700, y: 900 })); game.setChildIndex(upperLeftConnector, 1); var upperRightConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 3, x: 1350, y: 900 })); game.setChildIndex(upperRightConnector, 1); // Add connecting corridors between lower and main areas var lowerLeftConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 3, x: 700, y: 1800 })); game.setChildIndex(lowerLeftConnector, 1); var lowerRightConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 3, x: 1350, y: 1800 })); game.setChildIndex(lowerRightConnector, 1); // Add horizontal connectors to main corridor var mainLeftConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5, x: 800, y: 1366 })); game.setChildIndex(mainLeftConnector, 1); var mainRightConnector = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5, x: 1250, y: 1366 })); game.setChildIndex(mainRightConnector, 1); // Add cafeteria/meeting room corridor var cafeteriaCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 2, x: 1024, y: 1100 })); game.setChildIndex(cafeteriaCorridor, 1); // Add storage area corridors var storageLeftCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2, x: 400, y: 1100 })); game.setChildIndex(storageLeftCorridor, 1); var storageRightCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2, x: 1650, y: 1100 })); game.setChildIndex(storageRightCorridor, 1); // Add engine room access corridors var engineAccessLeft = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2, x: 500, y: 2200 })); game.setChildIndex(engineAccessLeft, 1); var engineAccessRight = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2, x: 1550, y: 2200 })); game.setChildIndex(engineAccessRight, 1); // Add navigation/helm corridor var navigationCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5, x: 1024, y: 500 })); game.setChildIndex(navigationCorridor, 1); // Add T-junction corridors for better connectivity var upperTJunction = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, x: 1024, y: 800 })); game.setChildIndex(upperTJunction, 1); var lowerTJunction = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, x: 1024, y: 1900 })); game.setChildIndex(lowerTJunction, 1); // Add side corridor extensions var leftSideCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 6, x: 300, y: 1366 })); game.setChildIndex(leftSideCorridor, 1); var rightSideCorridor = game.addChild(LK.getAsset('shipHall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 6, x: 1750, y: 1366 })); game.setChildIndex(rightSideCorridor, 1); } // Create emergency button var emergencyButton = game.addChild(LK.getAsset('emergencyButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 })); emergencyButton.down = function (x, y, obj) { if (gamePhase === 'playing' && meetingCooldown <= 0) { startEmergencyMeeting(); } }; // Create kill button (initially hidden) killButton = LK.getAsset('killButton', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); killButton.alpha = 0; killButton.visible = false; LK.gui.center.addChild(killButton); var killButtonText = new Text2('KILL', { size: 40, fill: 0xFFFFFF }); killButtonText.anchor.set(0.5, 0.5); killButton.addChild(killButtonText); killButton.down = function (x, y, obj) { if (killTarget && currentPlayer && currentPlayer.isImpostor && currentPlayer.canKill()) { if (currentPlayer.kill(killTarget)) { updatePlayerCount(); hideKillButton(); } } }; // Create sabotage button (initially hidden) sabotageButton = LK.getAsset('sabotageButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 0 }); sabotageButton.alpha = 0; sabotageButton.visible = false; LK.gui.center.addChild(sabotageButton); // Create skip voting button (initially hidden) var skipVotingButton = LK.getAsset('voteButton', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 100 }); skipVotingButton.alpha = 0; skipVotingButton.visible = false; skipVotingButton.tint = 0xffff00; LK.gui.center.addChild(skipVotingButton); var skipVotingButtonText = new Text2('SKIP VOTING', { size: 36, fill: 0x000000 }); skipVotingButtonText.anchor.set(0.5, 0.5); skipVotingButton.addChild(skipVotingButtonText); var sabotageButtonText = new Text2('SABOTAGE', { size: 32, fill: 0xFFFFFF }); sabotageButtonText.anchor.set(0.5, 0.5); sabotageButton.addChild(sabotageButtonText); // Create report button (initially hidden) reportButton = LK.getAsset('reportButton', { anchorX: 0.5, anchorY: 0.5, x: -200, y: 0 }); reportButton.alpha = 0; reportButton.visible = false; reportButton.tint = 0x00ff00; LK.gui.center.addChild(reportButton); var reportButtonText = new Text2('REPORT', { size: 32, fill: 0xFFFFFF }); reportButtonText.anchor.set(0.5, 0.5); reportButton.addChild(reportButtonText); // Create task button (initially hidden) taskButton = LK.getAsset('taskButton', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150 }); taskButton.alpha = 0; taskButton.visible = false; taskButton.tint = 0x00ffff; LK.gui.center.addChild(taskButton); var taskButtonText = new Text2('DO TASK', { size: 32, fill: 0xFFFFFF }); taskButtonText.anchor.set(0.5, 0.5); taskButton.addChild(taskButtonText); // Create camera button (initially hidden) cameraButton = LK.getAsset('cameraButton', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 0 }); cameraButton.alpha = 0; cameraButton.visible = false; cameraButton.tint = 0x00ff00; LK.gui.center.addChild(cameraButton); var cameraButtonText = new Text2('CAMERAS', { size: 28, fill: 0xFFFFFF }); cameraButtonText.anchor.set(0.5, 0.5); cameraButton.addChild(cameraButtonText); // Create door button (initially hidden) doorButton = LK.getAsset('doorButton', { anchorX: 0.5, anchorY: 0.5, x: -300, y: 0 }); doorButton.alpha = 0; doorButton.visible = false; doorButton.tint = 0xff0000; LK.gui.center.addChild(doorButton); var doorButtonText = new Text2('DOORS', { size: 28, fill: 0xFFFFFF }); doorButtonText.anchor.set(0.5, 0.5); doorButton.addChild(doorButtonText); // Create individual sabotage buttons (initially hidden) sabotageElectricityButton = LK.getAsset('sabotageElectricityButton', { anchorX: 0.5, anchorY: 0.5, x: -200, y: -100 }); sabotageElectricityButton.alpha = 0; sabotageElectricityButton.visible = false; sabotageElectricityButton.tint = 0xffff00; LK.gui.center.addChild(sabotageElectricityButton); var sabotageElectricityButtonText = new Text2('ELECTRICITY', { size: 20, fill: 0x000000 }); sabotageElectricityButtonText.anchor.set(0.5, 0.5); sabotageElectricityButton.addChild(sabotageElectricityButtonText); sabotageOxygenButton = LK.getAsset('sabotageOxygenButton', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -100 }); sabotageOxygenButton.alpha = 0; sabotageOxygenButton.visible = false; sabotageOxygenButton.tint = 0x00ffff; LK.gui.center.addChild(sabotageOxygenButton); var sabotageOxygenButtonText = new Text2('OXYGEN', { size: 20, fill: 0x000000 }); sabotageOxygenButtonText.anchor.set(0.5, 0.5); sabotageOxygenButton.addChild(sabotageOxygenButtonText); sabotageDoorsButton = LK.getAsset('sabotageDoorsButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: -100 }); sabotageDoorsButton.alpha = 0; sabotageDoorsButton.visible = false; sabotageDoorsButton.tint = 0xff8800; LK.gui.center.addChild(sabotageDoorsButton); var sabotageDoorsButtonText = new Text2('DOORS', { size: 20, fill: 0x000000 }); sabotageDoorsButtonText.anchor.set(0.5, 0.5); sabotageDoorsButton.addChild(sabotageDoorsButtonText); sabotageReactorButton = LK.getAsset('sabotageReactorButton', { anchorX: 0.5, anchorY: 0.5, x: -100, y: -200 }); sabotageReactorButton.alpha = 0; sabotageReactorButton.visible = false; sabotageReactorButton.tint = 0xff0000; LK.gui.center.addChild(sabotageReactorButton); var sabotageReactorButtonText = new Text2('REACTOR', { size: 20, fill: 0x000000 }); sabotageReactorButtonText.anchor.set(0.5, 0.5); sabotageReactorButton.addChild(sabotageReactorButtonText); sabotageConnectionsButton = LK.getAsset('sabotageConnectionsButton', { anchorX: 0.5, anchorY: 0.5, x: 100, y: -200 }); sabotageConnectionsButton.alpha = 0; sabotageConnectionsButton.visible = false; sabotageConnectionsButton.tint = 0x8800ff; LK.gui.center.addChild(sabotageConnectionsButton); var sabotageConnectionsButtonText = new Text2('CONNECTIONS', { size: 18, fill: 0x000000 }); sabotageConnectionsButtonText.anchor.set(0.5, 0.5); sabotageConnectionsButton.addChild(sabotageConnectionsButtonText); sabotageButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing') { performSabotage(); hideSabotageButton(); } }; // Individual sabotage button handlers sabotageElectricityButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { performSpecificSabotage('electricity'); hideIndividualSabotageButtons(); } }; sabotageOxygenButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { performSpecificSabotage('oxygen'); hideIndividualSabotageButtons(); } }; sabotageDoorsButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { performSpecificSabotage('doors'); hideIndividualSabotageButtons(); } }; sabotageReactorButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { performSpecificSabotage('reactor'); hideIndividualSabotageButtons(); } }; sabotageConnectionsButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { performSpecificSabotage('communications'); hideIndividualSabotageButtons(); } }; reportButton.down = function (x, y, obj) { if (reportTarget && currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') { reportDeadBody(); hideReportButton(); } }; skipVotingButton.down = function (x, y, obj) { if (votingPhase && gamePhase === 'voting') { endVoting(); } }; taskButton.down = function (x, y, obj) { if (currentTaskTarget && currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor && gamePhase === 'playing') { currentTaskTarget.startTask(); } }; cameraButton.down = function (x, y, obj) { if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing' && securityRoom) { // Check if player is near security room var distance = Math.sqrt(Math.pow(currentPlayer.x - securityRoom.x, 2) + Math.pow(currentPlayer.y - securityRoom.y, 2)); if (distance < 150) { if (!securityRoom.isActive) { securityRoom.activate(); } } } }; // Mission panel helper functions function toggleMissionPanel() { missionPanelVisible = !missionPanelVisible; missionPanel.visible = missionPanelVisible; missionPanelTitle.visible = missionPanelVisible; if (missionPanelVisible) { updateMissionPanel(); // Show panel with animation tween(missionPanel, { alpha: 0.9, scaleX: 2.5, scaleY: 3.0 }, { duration: 300 }); } else { // Hide panel with animation tween(missionPanel, { alpha: 0.8, scaleX: 2.3, scaleY: 2.8 }, { duration: 300, onFinish: function onFinish() { missionTaskTexts.forEach(function (text) { if (text) text.visible = false; }); } }); } } function updateMissionPanel() { if (!currentPlayer || currentPlayer.isImpostor || !missionPanelVisible) return; // Clear existing task texts missionTaskTexts.forEach(function (text) { if (text) text.destroy(); }); missionTaskTexts = []; // Show assigned tasks var yOffset = 280; var taskIndex = 0; if (currentPlayer.assignedTasks && currentPlayer.assignedTasks.length > 0) { currentPlayer.assignedTasks.forEach(function (task) { if (taskIndex >= 8) return; // Limit to 8 tasks to fit in panel var statusIcon = task.isCompleted ? '✓' : '○'; var statusColor = task.isCompleted ? 0x00FF00 : 0xFFFF00; var taskText = new Text2(statusIcon + ' ' + task.taskName, { size: 24, fill: statusColor }); taskText.anchor.set(0, 0); taskText.x = 80; taskText.y = yOffset; taskText.visible = true; LK.gui.topLeft.addChild(taskText); missionTaskTexts.push(taskText); // Add task location hint var locationText = new Text2('Location: ' + getTaskLocationName(task), { size: 16, fill: 0xAAAAAAA }); locationText.anchor.set(0, 0); locationText.x = 100; locationText.y = yOffset + 30; locationText.visible = true; LK.gui.topLeft.addChild(locationText); missionTaskTexts.push(locationText); yOffset += 60; taskIndex++; }); } else { // Show message if no tasks assigned var noTasksText = new Text2('No tasks assigned', { size: 20, fill: 0xFFFFFF }); noTasksText.anchor.set(0, 0); noTasksText.x = 80; noTasksText.y = 280; noTasksText.visible = true; LK.gui.topLeft.addChild(noTasksText); missionTaskTexts.push(noTasksText); } // Show completion progress var completedCount = currentPlayer.assignedTasks ? currentPlayer.assignedTasks.filter(function (task) { return task.isCompleted; }).length : 0; var totalAssigned = currentPlayer.assignedTasks ? currentPlayer.assignedTasks.length : 0; var progressText = new Text2('Progress: ' + completedCount + '/' + totalAssigned, { size: 20, fill: 0x00FFFF }); progressText.anchor.set(0.5, 0); progressText.x = 250; progressText.y = 260; progressText.visible = true; LK.gui.topLeft.addChild(progressText); missionTaskTexts.push(progressText); } function getTaskLocationName(task) { // Return location name based on task position if (task.x < 500) { if (task.y < 800) return 'Upper Left'; if (task.y > 1800) return 'Lower Left'; return 'Left Wing'; } else if (task.x > 1500) { if (task.y < 800) return 'Upper Right'; if (task.y > 1800) return 'Lower Right'; return 'Right Wing'; } else { if (task.y < 800) return 'Command Bridge'; if (task.y > 1800) return 'Cargo Bay'; return 'Central Area'; } } // Role selection button handlers crewButton.down = function (x, y, obj) { if (gamePhase === 'roleSelection') { selectRole(false); // false = crew member } }; impostorButton.down = function (x, y, obj) { if (gamePhase === 'roleSelection') { selectRole(true); // true = impostor } }; // Initialize players function selectRole(isImpostor) { // Hide role selection UI roleSelectionTitle.destroy(); crewButton.destroy(); crewButtonText.destroy(); impostorButton.destroy(); impostorButtonText.destroy(); instructionText.destroy(); // Initialize game with selected role initializePlayers(isImpostor); updatePlayerCount(); gamePhase = 'playing'; phaseText.setText('Complete Tasks or Find Impostors!'); } function initializePlayers(playerIsImpostor) { var colors = [0x00ff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffa500, 0xffff00, 0x8a2be2, 0xffc0cb]; // Create current player first with selected role currentPlayer = new Player(playerIsImpostor, colors[0], 0); // Generate safe spawn position for current player var validPosition = findValidSpawnPosition(); currentPlayer.x = validPosition.x; currentPlayer.y = validPosition.y; currentPlayer.targetX = currentPlayer.x; currentPlayer.targetY = currentPlayer.y; players.push(currentPlayer); game.addChild(currentPlayer); // Create other players var remainingImpostors = playerIsImpostor ? impostorCount - 1 : impostorCount; for (var i = 1; i < 8; i++) { var isImpostor = i <= remainingImpostors; var player = new Player(isImpostor, colors[i], i); // Generate safe spawn position var validPosition = findValidSpawnPosition(); player.x = validPosition.x; player.y = validPosition.y; player.targetX = player.x; player.targetY = player.y; players.push(player); game.addChild(player); } } // Initialize tasks function initializeTasks() { var taskTypes = ['electrical', 'engine', 'reactor', 'navigation', 'medical', 'cables']; var taskAreas = [ // Command Bridge - New room { x: 900, y: 350, width: 250, height: 150, preferredType: 'navigation' }, // Communication Array - New room { x: 1500, y: 525, width: 200, height: 125, preferredType: 'electrical' }, // Laboratory - New room { x: 250, y: 1000, width: 200, height: 200, preferredType: 'medical' }, // Weapons Room - New room { x: 1750, y: 1000, width: 200, height: 200, preferredType: 'reactor' }, // Maintenance Tunnels - New room { x: 350, y: 1925, width: 200, height: 125, preferredType: 'cables' }, // Cargo Bay - New room { x: 900, y: 2350, width: 400, height: 200, preferredType: 'engine' }, // Electrical room { x: 400, y: 500, width: 200, height: 150, preferredType: 'electrical' }, // Engine room upper { x: 1200, y: 500, width: 200, height: 150, preferredType: 'engine' }, // Security room { x: 800, y: 800, width: 300, height: 200, preferredType: 'navigation' }, // Medical bay { x: 1200, y: 1200, width: 250, height: 180, preferredType: 'medical' }, // Reactor room { x: 400, y: 1200, width: 250, height: 180, preferredType: 'reactor' }, // Lower engine room { x: 1200, y: 2000, width: 200, height: 150, preferredType: 'engine' }, // Lower electrical { x: 400, y: 2000, width: 200, height: 150, preferredType: 'electrical' }, // Central corridor tasks { x: 800, y: 1600, width: 400, height: 200, preferredType: 'navigation' }, // Central area // Cables maintenance room { x: 600, y: 1000, width: 200, height: 150, preferredType: 'cables' }]; // Get crew members (non-impostors) var crewMembers = players.filter(function (player) { return !player.isImpostor; }); // Create tasks with individual assignments var taskTypeCount = {}; taskTypes.forEach(function (type) { taskTypeCount[type] = 0; }); var taskId = 0; // Create tasks for each crew member crewMembers.forEach(function (player) { var playerTaskCount = {}; taskTypes.forEach(function (type) { playerTaskCount[type] = 0; }); // Assign tasks to this player var tasksPerPlayer = Math.floor(totalTasks / crewMembers.length); if (player.playerId < totalTasks % crewMembers.length) { tasksPerPlayer++; // Distribute remaining tasks } for (var i = 0; i < tasksPerPlayer; i++) { // Distribute tasks across different areas var area = taskAreas[taskId % taskAreas.length]; var taskX = area.x + Math.random() * area.width; var taskY = area.y + Math.random() * area.height; // Prefer area's specialized task type, but ensure variety for this player var taskType = area.preferredType; if (playerTaskCount[taskType] >= 2) { // Too many of this type for this player, pick a different one var availableTypes = taskTypes.filter(function (type) { return playerTaskCount[type] < 2; }); if (availableTypes.length > 0) { taskType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; } } playerTaskCount[taskType]++; taskTypeCount[taskType]++; var task; if (taskType === 'cables') { task = new CablesTask(taskX, taskY, taskId, player.playerId); } else { task = new Task(taskX, taskY, taskId, taskType, player.playerId); } // Add task to player's assigned tasks player.assignedTasks.push(task); tasks.push(task); game.addChild(task); taskId++; } }); } function updateTaskProgress() { taskProgressText.setText('Tasks: ' + completedTasks + '/' + totalTasks); // Update progress bar var progressPercentage = completedTasks / totalTasks; progressBarFill.scaleX = progressPercentage * 8; // Show task completion animation if (taskCompletionText) { taskCompletionText.setText('Task Completed! (' + completedTasks + '/' + totalTasks + ')'); taskCompletionText.alpha = 1; taskCompletionText.y = 0; tween(taskCompletionText, { y: -100, alpha: 0 }, { duration: 2000 }); } // Flash progress bar tween(progressBarFill, { tint: 0xFFFF00 }, { duration: 200, onFinish: function onFinish() { tween(progressBarFill, { tint: 0x00FF00 }, { duration: 200 }); } }); // Check win condition if (completedTasks >= totalTasks) { endGame('crew'); } } function updatePlayerCount() { var aliveCrew = players.filter(function (p) { return p.isAlive && !p.isImpostor; }).length; var aliveImpostors = players.filter(function (p) { return p.isAlive && p.isImpostor; }).length; playerCountText.setText('Crew: ' + aliveCrew + ' | Impostors: ' + aliveImpostors); // Check win conditions if (aliveImpostors >= aliveCrew) { endGame('impostors'); } else if (aliveImpostors === 0) { endGame('crew'); } } function startEmergencyMeeting() { gamePhase = 'voting'; votingPhase = true; meetingCooldown = meetingCooldownTime; LK.getSound('emergency').play(); phaseText.setText('Emergency Meeting - Vote!'); // Create vote buttons var alivePlayers = players.filter(function (p) { return p.isAlive; }); var buttonWidth = 200; var startX = (2048 - alivePlayers.length * buttonWidth) / 2; for (var i = 0; i < alivePlayers.length; i++) { var button = new VoteButton(alivePlayers[i].playerId, startX + i * buttonWidth, 2400); voteButtons.push(button); game.addChild(button); } // Reset player voting ability players.forEach(function (p) { if (p.isAlive) { p.canVote = true; } }); // Show skip voting button if (skipVotingButton) { skipVotingButton.alpha = 1; skipVotingButton.visible = true; } } function endVoting() { votingPhase = false; // Find player with most votes var maxVotes = 0; var ejectedPlayer = null; for (var i = 0; i < voteButtons.length; i++) { if (voteButtons[i].votes > maxVotes) { maxVotes = voteButtons[i].votes; ejectedPlayer = players.find(function (p) { return p.playerId === voteButtons[i].playerId; }); } } // Eject player with most votes if (ejectedPlayer && maxVotes > 0) { ejectedPlayer.eliminate(); if (ejectedPlayer.isImpostor) { phaseText.setText('Impostor Ejected!'); } else { phaseText.setText('Innocent Ejected!'); } } else { phaseText.setText('No One Ejected!'); } // Clean up vote buttons voteButtons.forEach(function (button) { button.destroy(); }); voteButtons = []; // Hide skip voting button if (skipVotingButton) { skipVotingButton.alpha = 0; skipVotingButton.visible = false; } // Return to playing phase after delay LK.setTimeout(function () { gamePhase = 'playing'; phaseText.setText('Complete Tasks or Find Impostors!'); updatePlayerCount(); }, 3000); } function showKillButton(target) { if (killButton && currentPlayer && currentPlayer.isImpostor) { killTarget = target; killButton.alpha = 1; killButton.visible = true; // Flash effect to draw attention tween(killButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(killButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } } function hideKillButton() { if (killButton) { killTarget = null; killButton.alpha = 0; killButton.visible = false; } } function showSabotageButton() { if (sabotageButton && currentPlayer && currentPlayer.isImpostor) { sabotageButton.alpha = 1; sabotageButton.visible = true; // Flash effect to draw attention tween(sabotageButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(sabotageButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } } function hideSabotageButton() { if (sabotageButton) { sabotageTarget = null; sabotageButton.alpha = 0; sabotageButton.visible = false; } } function showReportButton(target) { if (reportButton && currentPlayer && currentPlayer.isAlive) { reportTarget = target; reportButton.alpha = 1; reportButton.visible = true; // Flash effect to draw attention tween(reportButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(reportButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } } function hideReportButton() { if (reportButton) { reportTarget = null; reportButton.alpha = 0; reportButton.visible = false; } } function showTaskButton(target) { if (taskButton && currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor) { currentTaskTarget = target; taskButton.alpha = 1; taskButton.visible = true; // Flash effect to draw attention tween(taskButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(taskButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } } function hideTaskButton() { if (taskButton) { currentTaskTarget = null; taskButton.alpha = 0; taskButton.visible = false; } } function showCameraButton() { if (cameraButton && currentPlayer && currentPlayer.isAlive) { cameraButton.alpha = 1; cameraButton.visible = true; // Flash effect to draw attention tween(cameraButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(cameraButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } } function hideCameraButton() { if (cameraButton) { cameraButton.alpha = 0; cameraButton.visible = false; } } function reportDeadBody() { if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') { // Play report sound LK.getSound('report').play(); // Start emergency meeting startEmergencyMeeting(); // Update phase text to indicate it was a report phaseText.setText('Dead Body Reported - Vote!'); } } function performSabotage() { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { // Show individual sabotage buttons instead of performing random sabotage showIndividualSabotageButtons(); } } function performSpecificSabotage(sabotageType) { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { // Play sabotage sound LK.getSound('sabotage').play(); // Activate specific sabotage type activateSabotage(sabotageType); } } function showIndividualSabotageButtons() { if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { // Show all individual sabotage buttons sabotageElectricityButton.alpha = 1; sabotageElectricityButton.visible = true; sabotageOxygenButton.alpha = 1; sabotageOxygenButton.visible = true; sabotageDoorsButton.alpha = 1; sabotageDoorsButton.visible = true; sabotageReactorButton.alpha = 1; sabotageReactorButton.visible = true; sabotageConnectionsButton.alpha = 1; sabotageConnectionsButton.visible = true; // Hide main sabotage button hideSabotageButton(); // Add pulsing animation to draw attention tween(sabotageElectricityButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, onFinish: function onFinish() { tween(sabotageElectricityButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300 }); } }); } } function hideIndividualSabotageButtons() { if (sabotageElectricityButton) { sabotageElectricityButton.alpha = 0; sabotageElectricityButton.visible = false; } if (sabotageOxygenButton) { sabotageOxygenButton.alpha = 0; sabotageOxygenButton.visible = false; } if (sabotageDoorsButton) { sabotageDoorsButton.alpha = 0; sabotageDoorsButton.visible = false; } if (sabotageReactorButton) { sabotageReactorButton.alpha = 0; sabotageReactorButton.visible = false; } if (sabotageConnectionsButton) { sabotageConnectionsButton.alpha = 0; sabotageConnectionsButton.visible = false; } } function activateSabotage(sabotageType) { if (isSabotageActive) return; isSabotageActive = true; activeSabotage = sabotageType; sabotageTimer = 30000; // 30 seconds to fix // Hide individual sabotage buttons hideIndividualSabotageButtons(); // Hide task progress bar during sabotage taskProgressText.alpha = 0; progressBarBackground.alpha = 0; progressBarFill.alpha = 0; // Disable cameras during sabotage if (securityRoom && securityRoom.isActive) { securityRoom.deactivate(); } cameras.forEach(function (camera) { camera.isActive = false; }); // Apply sabotage effects based on type switch (sabotageType) { case 'electricity': // Power outage - darken screen sabotageEffect.alpha = 0.7; sabotageEffect.visible = true; sabotageEffect.tint = 0x000000; phaseText.setText('POWER OUTAGE! Fix electrical systems!'); phaseText.tint = 0xff0000; break; case 'oxygen': // Oxygen depletion - blue tint sabotageEffect.alpha = 0.3; sabotageEffect.visible = true; sabotageEffect.tint = 0x0000ff; phaseText.setText('OXYGEN DEPLETED! Restore life support!'); phaseText.tint = 0x0000ff; break; case 'reactor': // Reactor meltdown - red tint and flash sabotageEffect.alpha = 0.4; sabotageEffect.visible = true; sabotageEffect.tint = 0xff0000; phaseText.setText('REACTOR CRITICAL! Fix reactor core!'); phaseText.tint = 0xff0000; // Flash effect tween(sabotageEffect, { alpha: 0.6 }, { duration: 500, onFinish: function onFinish() { tween(sabotageEffect, { alpha: 0.4 }, { duration: 500 }); } }); break; case 'doors': // Close all doors doors.forEach(function (door) { if (door.isOpen) { door.close(); } }); phaseText.setText('DOORS SABOTAGED! Systems compromised!'); phaseText.tint = 0xff8800; break; case 'communications': // Disable task visibility tasks.forEach(function (task) { task.alpha = 0.3; }); phaseText.setText('COMMUNICATIONS DOWN! Tasks hidden!'); phaseText.tint = 0xffff00; break; } // Create sabotage fix indicators createSabotageIndicators(sabotageType); } function createSabotageIndicators(sabotageType) { // Clear existing indicators sabotageIndicators.forEach(function (indicator) { indicator.destroy(); }); sabotageIndicators = []; // Create fix locations based on sabotage type var fixLocations = []; switch (sabotageType) { case 'electricity': fixLocations = [{ x: 400, y: 500 }, // Upper electrical { x: 400, y: 2000 } // Lower electrical ]; break; case 'oxygen': fixLocations = [{ x: 1200, y: 1200 }, // Medical bay { x: 850, y: 1500 } // Reactor area ]; break; case 'reactor': fixLocations = [{ x: 850, y: 1500 }, // Reactor room { x: 1200, y: 500 } // Engine room ]; break; case 'doors': fixLocations = [{ x: 1024, y: 600 }, // Security room { x: 1024, y: 1366 } // Central corridor ]; break; case 'communications': fixLocations = [{ x: 1024, y: 600 }, // Security room { x: 1024, y: 800 } // Admin area ]; break; } // Create visual indicators fixLocations.forEach(function (location) { var indicator = game.addChild(LK.getAsset('sabotageIndicator', { anchorX: 0.5, anchorY: 0.5, x: location.x, y: location.y })); indicator.fixLocation = location; sabotageIndicators.push(indicator); // Pulsing animation tween(indicator, { scaleX: 1.5, scaleY: 1.5, alpha: 0.7 }, { duration: 1000, onFinish: function onFinish() { tween(indicator, { scaleX: 1.0, scaleY: 1.0, alpha: 1.0 }, { duration: 1000 }); } }); }); } function fixSabotage() { if (!isSabotageActive) return; isSabotageActive = false; activeSabotage = null; sabotageTimer = 0; // Restore task progress bar taskProgressText.alpha = 1; progressBarBackground.alpha = 1; progressBarFill.alpha = 1; // Re-enable cameras cameras.forEach(function (camera) { camera.isActive = true; }); // Hide sabotage effect sabotageEffect.alpha = 0; sabotageEffect.visible = false; // Restore task visibility tasks.forEach(function (task) { task.alpha = 1.0; }); // Reset phase text phaseText.setText('Complete Tasks or Find Impostors!'); phaseText.tint = 0xFFFF00; // Clear sabotage indicators sabotageIndicators.forEach(function (indicator) { indicator.destroy(); }); sabotageIndicators = []; // Play success sound LK.getSound('taskComplete').play(); } // Find valid spawn position that doesn't collide with walls function findValidSpawnPosition() { var playerSize = 80; var maxAttempts = 50; var attempts = 0; while (attempts < maxAttempts) { var x = 300 + Math.random() * 1400; var y = 300 + Math.random() * 2000; // Check if this position is valid (not inside a wall) if (!checkWallCollision(x, y, playerSize, playerSize)) { return { x: x, y: y }; } attempts++; } // If we can't find a valid position after many attempts, use a known safe position // Return center of main corridor as fallback return { x: 1024, y: 1366 }; } // Collision detection function function checkWallCollision(x, y, playerWidth, playerHeight) { var playerLeft = x - playerWidth / 2; var playerRight = x + playerWidth / 2; var playerTop = y - playerHeight / 2; var playerBottom = y + playerHeight / 2; for (var i = 0; i < walls.length; i++) { var wall = walls[i]; var wallLeft = wall.x - wall.width / 2; var wallRight = wall.x + wall.width / 2; var wallTop = wall.y - wall.height / 2; var wallBottom = wall.y + wall.height / 2; // Check if player rectangle intersects with wall rectangle if (playerLeft < wallRight && playerRight > wallLeft && playerTop < wallBottom && playerBottom > wallTop) { return true; } } // Check door collisions for (var i = 0; i < doors.length; i++) { var door = doors[i]; if (door.blocksMovement(x, y, playerWidth, playerHeight)) { return true; } } return false; } function endGame(winner) { gamePhase = 'gameOver'; if (winner === 'crew') { phaseText.setText('Crew Wins!'); LK.showYouWin(); } else { phaseText.setText('Impostors Win!'); LK.showGameOver(); } } // AI behavior for other players function updateAI() { players.forEach(function (player) { if (player === currentPlayer || !player.isAlive) return; // Crew member AI - complete tasks if (!player.isImpostor && gamePhase === 'playing') { // Find nearby incomplete tasks assigned to this player var nearbyTasks = player.assignedTasks.filter(function (task) { if (task.isCompleted) return false; var distance = Math.sqrt(Math.pow(task.x - player.x, 2) + Math.pow(task.y - player.y, 2)); return distance < 100; }); // If near a task, complete it if (nearbyTasks.length > 0) { var task = nearbyTasks[0]; task.completionProgress++; // Complete task if enough progress if (task.completionProgress >= task.requiredClicks) { task.complete(); player.completeTask(); completedTasks++; updateTaskProgress(); } } else { // Move towards nearest incomplete task assigned to this player var incompleteTasks = player.assignedTasks.filter(function (task) { return !task.isCompleted; }); if (incompleteTasks.length > 0) { // Find closest assigned task var closestTask = incompleteTasks[0]; var closestDistance = Math.sqrt(Math.pow(closestTask.x - player.x, 2) + Math.pow(closestTask.y - player.y, 2)); for (var i = 1; i < incompleteTasks.length; i++) { var task = incompleteTasks[i]; var distance = Math.sqrt(Math.pow(task.x - player.x, 2) + Math.pow(task.y - player.y, 2)); if (distance < closestDistance) { closestTask = task; closestDistance = distance; } } // Move towards closest assigned task with some randomness if (Math.random() < 0.1) { player.moveTo(closestTask.x + (Math.random() - 0.5) * 50, closestTask.y + (Math.random() - 0.5) * 50); } } } } // Random movement for all players (less frequent now) else if (Math.random() < 0.01) { player.moveTo(300 + Math.random() * 1400, 300 + Math.random() * 2000); } // Impostor AI if (player.isImpostor && gamePhase === 'playing') { // Try to kill nearby crew members var nearbyTargets = players.filter(function (p) { if (p === player || !p.isAlive || p.isImpostor) return false; var distance = Math.sqrt(Math.pow(p.x - player.x, 2) + Math.pow(p.y - player.y, 2)); return distance < 120; }); if (nearbyTargets.length > 0 && player.canKill()) { // AI impostor kills with some probability to make it more realistic if (Math.random() < 0.3) { // 30% chance to kill when opportunity arises player.kill(nearbyTargets[0]); updatePlayerCount(); } } else { // Move towards nearest crew member to hunt them var crewMembers = players.filter(function (p) { if (p === player || !p.isAlive || p.isImpostor) return false; return true; }); if (crewMembers.length > 0) { // Find closest crew member var closestCrew = crewMembers[0]; var closestDistance = Math.sqrt(Math.pow(closestCrew.x - player.x, 2) + Math.pow(closestCrew.y - player.y, 2)); for (var i = 1; i < crewMembers.length; i++) { var crew = crewMembers[i]; var distance = Math.sqrt(Math.pow(crew.x - player.x, 2) + Math.pow(crew.y - player.y, 2)); if (distance < closestDistance) { closestCrew = crew; closestDistance = distance; } } // Move towards closest crew member with some randomness (less frequent to avoid being too obvious) if (Math.random() < 0.05) { player.moveTo(closestCrew.x + (Math.random() - 0.5) * 100, closestCrew.y + (Math.random() - 0.5) * 100); } } } } }); } // Initialize cameras and security room function initializeCameras() { // Create security cameras in key locations var cameraPositions = [{ x: 400, y: 500, angle: 0 }, // Upper left electrical { x: 1600, y: 500, angle: Math.PI }, // Upper right engine { x: 1024, y: 800, angle: Math.PI / 2 }, // Security/Admin area { x: 400, y: 1500, angle: 0 }, // Lower left reactor { x: 1600, y: 1500, angle: Math.PI }, // Lower right medical { x: 1024, y: 2000, angle: Math.PI / 2 } // Lower corridor ]; for (var i = 0; i < cameraPositions.length; i++) { var camPos = cameraPositions[i]; var camera = new Camera(camPos.x, camPos.y, i, camPos.angle); cameras.push(camera); game.addChild(camera); } // Create security room in the upper central area securityRoom = new SecurityRoom(1024, 600); game.addChild(securityRoom); } // Initialize doors function initializeDoors() { // Create doors in strategic locations throughout the ship var doorPositions = [ // Upper corridor doors { x: 700, y: 500, orientation: 0 }, // Upper left entrance { x: 1350, y: 500, orientation: 0 }, // Upper right entrance // Main corridor doors { x: 550, y: 1200, orientation: 1 }, // Left side entrance { x: 1500, y: 1200, orientation: 1 }, // Right side entrance // Lower corridor doors { x: 700, y: 2000, orientation: 0 }, // Lower left entrance { x: 1350, y: 2000, orientation: 0 }, // Lower right entrance // Room entrance doors { x: 500, y: 800, orientation: 1 }, // Electrical room { x: 1550, y: 800, orientation: 1 }, // Engine room { x: 1024, y: 1000, orientation: 0 }, // Cafeteria entrance { x: 1024, y: 1700, orientation: 0 }, // Lower cafeteria entrance // Security and critical areas { x: 900, y: 600, orientation: 1 }, // Security room left { x: 1150, y: 600, orientation: 1 }, // Security room right // Storage and maintenance { x: 350, y: 1366, orientation: 1 }, // Left storage { x: 1700, y: 1366, orientation: 1 } // Right storage ]; for (var i = 0; i < doorPositions.length; i++) { var pos = doorPositions[i]; var door = new Door(pos.x, pos.y, i, pos.orientation); doors.push(door); game.addChild(door); } } // Initialize game createWalls(); initializeTasks(); initializeCameras(); initializeDoors(); // Don't initialize players yet - wait for role selection // Game controls game.down = function (x, y, obj) { if (gamePhase === 'playing' && currentPlayer && currentPlayer.isAlive) { // Check if clicking on sabotage fix location if (isSabotageActive && !currentPlayer.isImpostor) { var clickedIndicator = null; sabotageIndicators.forEach(function (indicator) { var distance = Math.sqrt(Math.pow(x - indicator.x, 2) + Math.pow(y - indicator.y, 2)); if (distance < 80) { clickedIndicator = indicator; } }); if (clickedIndicator) { // Fix sabotage fixSabotage(); return; } } currentPlayer.moveTo(x, y); } }; game.update = function () { if (gamePhase === 'roleSelection') { // Don't update game logic during role selection return; } // Hide sabotage button if not impostor or not playing if (!currentPlayer || !currentPlayer.isImpostor || gamePhase !== 'playing') { if (sabotageButton && sabotageButton.visible) { hideSabotageButton(); } // Also hide individual sabotage buttons hideIndividualSabotageButtons(); } // Hide report button if not playing if (!currentPlayer || !currentPlayer.isAlive || gamePhase !== 'playing') { if (reportButton && reportButton.visible) { hideReportButton(); } } // Hide task button if not playing or is impostor if (!currentPlayer || !currentPlayer.isAlive || currentPlayer.isImpostor || gamePhase !== 'playing') { if (taskButton && taskButton.visible) { hideTaskButton(); } } // Hide mission panel elements if not crew member or not playing if (!currentPlayer || !currentPlayer.isAlive || currentPlayer.isImpostor || gamePhase !== 'playing') { if (missionToggleButton && missionToggleButton.visible) { missionToggleButton.alpha = 0; missionToggleButton.visible = false; } if (missionPanelVisible) { missionPanelVisible = false; missionPanel.visible = false; missionPanelTitle.visible = false; missionTaskTexts.forEach(function (text) { if (text) text.visible = false; }); } } // Hide camera button if not playing if (!currentPlayer || !currentPlayer.isAlive || gamePhase !== 'playing') { if (cameraButton && cameraButton.visible) { hideCameraButton(); } } // Hide door button if not impostor or not playing if (!currentPlayer || !currentPlayer.isAlive || !currentPlayer.isImpostor || gamePhase !== 'playing') { if (doorButton && doorButton.visible) { hideDoorButton(); } } // Check line of sight between two points (returns true if clear, false if blocked by wall) function checkLineOfSight(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; var distance = Math.sqrt(dx * dx + dy * dy); // If points are very close, assume line of sight is clear if (distance < 10) return true; // Check multiple points along the line var steps = Math.ceil(distance / 20); // Check every 20 pixels for (var i = 0; i <= steps; i++) { var t = i / steps; var checkX = x1 + dx * t; var checkY = y1 + dy * t; // Check if this point intersects with any wall for (var j = 0; j < walls.length; j++) { var wall = walls[j]; var wallLeft = wall.x - wall.width / 2; var wallRight = wall.x + wall.width / 2; var wallTop = wall.y - wall.height / 2; var wallBottom = wall.y + wall.height / 2; // Check if point is inside wall if (checkX >= wallLeft && checkX <= wallRight && checkY >= wallTop && checkY <= wallBottom) { return false; // Line of sight blocked } } } return true; // Line of sight clear } // Update visibility of all game objects based on line of sight function updateVisibility() { if (!currentPlayer || !currentPlayer.isAlive) return; var playerX = currentPlayer.x; var playerY = currentPlayer.y; var visionRange = 300; // How far player can see // Update player visibility players.forEach(function (player) { if (player === currentPlayer) { player.alpha = 1.0; // Always see yourself return; } var distance = Math.sqrt(Math.pow(player.x - playerX, 2) + Math.pow(player.y - playerY, 2)); var hasLineOfSight = checkLineOfSight(playerX, playerY, player.x, player.y); if (distance <= visionRange && hasLineOfSight) { player.alpha = 1.0; // Fully visible } else { player.alpha = 0.0; // Hidden } }); // Update task visibility (only for crew members) if (!currentPlayer.isImpostor) { tasks.forEach(function (task) { var distance = Math.sqrt(Math.pow(task.x - playerX, 2) + Math.pow(task.y - playerY, 2)); var hasLineOfSight = checkLineOfSight(playerX, playerY, task.x, task.y); if (distance <= visionRange && hasLineOfSight) { task.alpha = task.isCompleted ? 0.5 : 1.0; // Visible } else { task.alpha = 0.0; // Hidden } }); } // Update camera visibility cameras.forEach(function (camera) { var distance = Math.sqrt(Math.pow(camera.x - playerX, 2) + Math.pow(camera.y - playerY, 2)); var hasLineOfSight = checkLineOfSight(playerX, playerY, camera.x, camera.y); if (distance <= visionRange && hasLineOfSight) { camera.alpha = 1.0; // Visible } else { camera.alpha = 0.0; // Hidden } }); // Update door visibility doors.forEach(function (door) { var distance = Math.sqrt(Math.pow(door.x - playerX, 2) + Math.pow(door.y - playerY, 2)); var hasLineOfSight = checkLineOfSight(playerX, playerY, door.x, door.y); if (distance <= visionRange && hasLineOfSight) { door.alpha = 1.0; // Visible } else { door.alpha = 0.0; // Hidden } }); // Update sabotage indicator visibility sabotageIndicators.forEach(function (indicator) { var distance = Math.sqrt(Math.pow(indicator.x - playerX, 2) + Math.pow(indicator.y - playerY, 2)); var hasLineOfSight = checkLineOfSight(playerX, playerY, indicator.x, indicator.y); if (distance <= visionRange && hasLineOfSight) { indicator.alpha = indicator.alpha || 1.0; // Preserve existing alpha for animations } else { indicator.alpha = 0.0; // Hidden } }); } // Update camera to follow current player if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') { // Calculate target camera position (center player on screen) var targetX = 1024 - currentPlayer.x; // Center horizontally var targetY = 1366 - currentPlayer.y; // Center vertically // Smooth camera movement with lerp var lerpFactor = 0.1; var currentX = game.x; var currentY = game.y; game.x = currentX + (targetX - currentX) * lerpFactor; game.y = currentY + (targetY - currentY) * lerpFactor; // Clamp camera to keep game world bounds in view var minX = 1024 - 1800; // Don't go past right edge var maxX = 1024 - 200; // Don't go past left edge var minY = 1366 - 2500; // Don't go past bottom edge var maxY = 1366 - 200; // Don't go past top edge game.x = Math.max(minX, Math.min(maxX, game.x)); game.y = Math.max(minY, Math.min(maxY, game.y)); // Update visibility every frame updateVisibility(); } // Update cooldowns if (meetingCooldown > 0) { meetingCooldown -= 16.67; // Roughly 1 frame at 60fps } // Update AI if (LK.ticks % 30 === 0) { // Update AI twice per second for better task completion updateAI(); } // Update current player impostor kill opportunities if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing') { var nearbyTargets = players.filter(function (p) { if (p === currentPlayer || !p.isAlive || p.isImpostor) return false; var distance = Math.sqrt(Math.pow(p.x - currentPlayer.x, 2) + Math.pow(p.y - currentPlayer.y, 2)); return distance < 120; }); // Show kill button if there are targets and can kill if (nearbyTargets.length > 0 && currentPlayer.canKill()) { // Add visual indicator for kill opportunity if (LK.ticks % 30 < 15) { nearbyTargets[0].alpha = 0.7; } else { nearbyTargets[0].alpha = 1.0; } // Show kill button for closest target if (!killButton.visible) { showKillButton(nearbyTargets[0]); } } else { // Hide kill button if no targets or can't kill if (killButton.visible) { hideKillButton(); } // Reset target alphas players.forEach(function (p) { if (!p.isImpostor) { p.alpha = 1.0; } }); } // Show sabotage button for impostors if (!sabotageButton.visible) { showSabotageButton(); } } // Update report button for all players near dead bodies if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') { var nearbyDeadBodies = players.filter(function (p) { if (p === currentPlayer || p.isAlive) return false; var distance = Math.sqrt(Math.pow(p.x - currentPlayer.x, 2) + Math.pow(p.y - currentPlayer.y, 2)); return distance < 150; }); // Show report button if there are dead bodies nearby if (nearbyDeadBodies.length > 0) { // Add visual indicator for dead body if (LK.ticks % 60 < 30) { nearbyDeadBodies[0].alpha = 0.5; } else { nearbyDeadBodies[0].alpha = 0.3; } // Show report button for closest dead body if (!reportButton.visible) { showReportButton(nearbyDeadBodies[0]); } } else { // Hide report button if no dead bodies nearby if (reportButton.visible) { hideReportButton(); } } // Update current task display for crew members and task button if (currentPlayer && !currentPlayer.isImpostor && currentPlayer.isAlive) { var nearbyTasks = currentPlayer.assignedTasks.filter(function (task) { if (task.isCompleted) return false; var distance = Math.sqrt(Math.pow(task.x - currentPlayer.x, 2) + Math.pow(task.y - currentPlayer.y, 2)); return distance < 100; }); if (nearbyTasks.length > 0) { var closestTask = nearbyTasks[0]; if (currentTaskText) { currentTaskText.setText(closestTask.taskName); currentTaskText.alpha = 0.8; } // Show task button for closest task if (!taskButton.visible) { showTaskButton(closestTask); } } else { if (currentTaskText) { currentTaskText.alpha = 0; } // Hide task button if no tasks nearby if (taskButton.visible) { hideTaskButton(); } } // Show mission toggle button for crew members if (missionToggleButton) { missionToggleButton.alpha = 0.8; missionToggleButton.visible = true; } // Update mission panel if visible if (missionPanelVisible && LK.ticks % 60 === 0) { updateMissionPanel(); } } // Update camera button for all players near security room if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing' && securityRoom) { var distanceToSecurity = Math.sqrt(Math.pow(currentPlayer.x - securityRoom.x, 2) + Math.pow(currentPlayer.y - securityRoom.y, 2)); // Show camera button if near security room if (distanceToSecurity < 150) { // Show camera button for security room access if (!cameraButton.visible) { showCameraButton(); } } else { // Hide camera button if not near security room if (cameraButton.visible) { hideCameraButton(); } } } // Update sabotage system if (isSabotageActive) { // Update sabotage timer sabotageTimer -= 16.67; // Roughly 1 frame at 60fps // Check if sabotage timer expired (crew loses) if (sabotageTimer <= 0) { endGame('impostors'); return; } // Update sabotage timer display var secondsLeft = Math.ceil(sabotageTimer / 1000); var currentText = phaseText.text || ''; phaseText.setText(currentText.split('(')[0] + '(' + secondsLeft + 's)'); // Check if player is near sabotage fix locations if (currentPlayer && currentPlayer.isAlive && !currentPlayer.isImpostor) { sabotageIndicators.forEach(function (indicator) { var distance = Math.sqrt(Math.pow(currentPlayer.x - indicator.x, 2) + Math.pow(currentPlayer.y - indicator.y, 2)); if (distance < 80) { // Player is close to fix location if (LK.ticks % 30 < 15) { indicator.alpha = 0.3; } else { indicator.alpha = 1.0; } // Allow fixing with click/tap indicator.canFix = true; } else { indicator.canFix = false; } }); } // Flash sabotage effect for critical sabotages if (activeSabotage === 'reactor' || activeSabotage === 'oxygen') { if (LK.ticks % 60 < 30) { sabotageEffect.alpha = sabotageEffect.alpha * 1.2; } else { sabotageEffect.alpha = sabotageEffect.alpha * 0.8; } } } // Update door button for impostors near doors (only if no sabotage active) if (currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor && gamePhase === 'playing' && !isSabotageActive) { var nearbyDoors = doors.filter(function (door) { var distance = Math.sqrt(Math.pow(currentPlayer.x - door.x, 2) + Math.pow(currentPlayer.y - door.y, 2)); return distance < 100; }); // Show door button if near doors if (nearbyDoors.length > 0) { var closestDoor = nearbyDoors[0]; // Show door button for closest door if (!doorButton.visible) { showDoorButton(closestDoor); } else if (currentDoorTarget !== closestDoor) { // Update target if different door is closer showDoorButton(closestDoor); } } else { // Hide door button if not near any doors if (doorButton.visible) { hideDoorButton(); } } } } };
===================================================================
--- original.js
+++ change.js
@@ -3609,8 +3609,99 @@
if (doorButton && doorButton.visible) {
hideDoorButton();
}
}
+ // Check line of sight between two points (returns true if clear, false if blocked by wall)
+ function checkLineOfSight(x1, y1, x2, y2) {
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ // If points are very close, assume line of sight is clear
+ if (distance < 10) return true;
+ // Check multiple points along the line
+ var steps = Math.ceil(distance / 20); // Check every 20 pixels
+ for (var i = 0; i <= steps; i++) {
+ var t = i / steps;
+ var checkX = x1 + dx * t;
+ var checkY = y1 + dy * t;
+ // Check if this point intersects with any wall
+ for (var j = 0; j < walls.length; j++) {
+ var wall = walls[j];
+ var wallLeft = wall.x - wall.width / 2;
+ var wallRight = wall.x + wall.width / 2;
+ var wallTop = wall.y - wall.height / 2;
+ var wallBottom = wall.y + wall.height / 2;
+ // Check if point is inside wall
+ if (checkX >= wallLeft && checkX <= wallRight && checkY >= wallTop && checkY <= wallBottom) {
+ return false; // Line of sight blocked
+ }
+ }
+ }
+ return true; // Line of sight clear
+ }
+ // Update visibility of all game objects based on line of sight
+ function updateVisibility() {
+ if (!currentPlayer || !currentPlayer.isAlive) return;
+ var playerX = currentPlayer.x;
+ var playerY = currentPlayer.y;
+ var visionRange = 300; // How far player can see
+ // Update player visibility
+ players.forEach(function (player) {
+ if (player === currentPlayer) {
+ player.alpha = 1.0; // Always see yourself
+ return;
+ }
+ var distance = Math.sqrt(Math.pow(player.x - playerX, 2) + Math.pow(player.y - playerY, 2));
+ var hasLineOfSight = checkLineOfSight(playerX, playerY, player.x, player.y);
+ if (distance <= visionRange && hasLineOfSight) {
+ player.alpha = 1.0; // Fully visible
+ } else {
+ player.alpha = 0.0; // Hidden
+ }
+ });
+ // Update task visibility (only for crew members)
+ if (!currentPlayer.isImpostor) {
+ tasks.forEach(function (task) {
+ var distance = Math.sqrt(Math.pow(task.x - playerX, 2) + Math.pow(task.y - playerY, 2));
+ var hasLineOfSight = checkLineOfSight(playerX, playerY, task.x, task.y);
+ if (distance <= visionRange && hasLineOfSight) {
+ task.alpha = task.isCompleted ? 0.5 : 1.0; // Visible
+ } else {
+ task.alpha = 0.0; // Hidden
+ }
+ });
+ }
+ // Update camera visibility
+ cameras.forEach(function (camera) {
+ var distance = Math.sqrt(Math.pow(camera.x - playerX, 2) + Math.pow(camera.y - playerY, 2));
+ var hasLineOfSight = checkLineOfSight(playerX, playerY, camera.x, camera.y);
+ if (distance <= visionRange && hasLineOfSight) {
+ camera.alpha = 1.0; // Visible
+ } else {
+ camera.alpha = 0.0; // Hidden
+ }
+ });
+ // Update door visibility
+ doors.forEach(function (door) {
+ var distance = Math.sqrt(Math.pow(door.x - playerX, 2) + Math.pow(door.y - playerY, 2));
+ var hasLineOfSight = checkLineOfSight(playerX, playerY, door.x, door.y);
+ if (distance <= visionRange && hasLineOfSight) {
+ door.alpha = 1.0; // Visible
+ } else {
+ door.alpha = 0.0; // Hidden
+ }
+ });
+ // Update sabotage indicator visibility
+ sabotageIndicators.forEach(function (indicator) {
+ var distance = Math.sqrt(Math.pow(indicator.x - playerX, 2) + Math.pow(indicator.y - playerY, 2));
+ var hasLineOfSight = checkLineOfSight(playerX, playerY, indicator.x, indicator.y);
+ if (distance <= visionRange && hasLineOfSight) {
+ indicator.alpha = indicator.alpha || 1.0; // Preserve existing alpha for animations
+ } else {
+ indicator.alpha = 0.0; // Hidden
+ }
+ });
+ }
// Update camera to follow current player
if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') {
// Calculate target camera position (center player on screen)
var targetX = 1024 - currentPlayer.x; // Center horizontally
@@ -3627,8 +3718,10 @@
var minY = 1366 - 2500; // Don't go past bottom edge
var maxY = 1366 - 200; // Don't go past top edge
game.x = Math.max(minX, Math.min(maxX, game.x));
game.y = Math.max(minY, Math.min(maxY, game.y));
+ // Update visibility every frame
+ updateVisibility();
}
// Update cooldowns
if (meetingCooldown > 0) {
meetingCooldown -= 16.67; // Roughly 1 frame at 60fps
crewmate among us. In-Game asset. 2d. High contrast. No shadows
electricalTask. In-Game asset. 2d. High contrast. No shadows
emergencyButton. In-Game asset. 2d. High contrast. No shadows
voteButton among us. In-Game asset. 2d. High contrast. No shadows
killButton among us. In-Game asset. 2d. High contrast. No shadows
medicalTask. In-Game asset. 2d. High contrast. No shadows
engineTask among us. In-Game asset. 2d. High contrast. No shadows
navigationTask among us. In-Game asset. 2d. High contrast. No shadows
shipFloor casella. In-Game asset. 2d. High contrast. No shadows
shiphall among us. In-Game asset. 2d. High contrast. No shadows
among us task. In-Game asset. 2d. High contrast. No shadows
wall among us. In-Game asset. 2d. High contrast. No shadows
taskCompleted among us. In-Game asset. 2d. High contrast. No shadows
reactorTask among us. In-Game asset. 2d. High contrast. No shadows
taskButton among us. In-Game asset. 2d. High contrast. No shadows
sabotageButton among us. In-Game asset. 2d. High contrast. No shadows
reportbutton among us. In-Game asset. 2d. High contrast. No shadows
cameraView among us. In-Game asset. 2d. High contrast. No shadows
cameraFrame among us. In-Game asset. 2d. High contrast. No shadows
cameraButton among us. In-Game asset. 2d. High contrast. No shadows
cablePanel among us. In-Game asset. 2d. High contrast. No shadows
cableWire among us. In-Game asset. 2d. High contrast. No shadows
among us door. In-Game asset. 2d. High contrast. No shadows
among us doorButton. In-Game asset. 2d. High contrast. No shadows
among us doorClosed. In-Game asset. 2d. High contrast. No shadows
among us sabotageEffect. In-Game asset. 2d. High contrast. No shadows
among us sabotageIndicator. In-Game asset. 2d. High contrast. No shadows
among us cableConnector. In-Game asset. 2d. High contrast. No shadows
among us sabotageReactorButton. In-Game asset. 2d. High contrast. No shadows
among us sabotageOxygenButton. In-Game asset. 2d. High contrast. No shadows
among us sabotageElectricityButton. In-Game asset. 2d. High contrast. No shadows
among us sabotageDoorsButton. In-Game asset. 2d. High contrast. No shadows
among us sabotageConnectionsButton. In-Game asset. 2d. High contrast. No shadows
among us sewerEntrance. In-Game asset. 2d. High contrast. No shadows