User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'split')' in or related to this line: 'phaseText.setText(phaseText.text.split('(')[0] + '(' + secondsLeft + 's)');' Line Number: 3141
User prompt
el impostor pot sabotear electricitat,oxigen,portes de la nau,reactor i conexions que fa que te oculti la barra de task i apaga las cameras
User prompt
fes les portes que el impostor pot tancar
User prompt
fes un boto per obrir las camaras
User prompt
fes las camaras i la sala de camaras
User prompt
fes la task de els cables
User prompt
fes un boto per fer las tasks
User prompt
fes els pasadisos de la nau del among us
User prompt
fes que el spawn no pugui apareixer en un mur
User prompt
fes las tasks de among us
User prompt
crea un boto per reportar
User prompt
fes que es pugui skip la votacio de emergencia
User prompt
crea el boto de sabotages per el impostor
User prompt
fes que el impostor mati a tripulants si nosaltres no ho som
User prompt
Please fix the bug: 'TypeError: taskGraphics.removeFromParent is not a function' in or related to this line: 'taskGraphics.removeFromParent();' Line Number: 182
User prompt
fes que els tripulants facin les tasques
User prompt
fes que els murs no es puguin atravesar
User prompt
Please fix the bug: 'TypeError: tween.to is not a function' in or related to this line: 'tween.to(killButton, {' Line Number: 814 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fes que el impostor pugui matar a els tripulants
User prompt
fes que la camara segueixi al jugador
User prompt
fes la nau de el among us
User prompt
pots triar en ser el impostor o un tripulant
User prompt
fes varias tasques per las misions
User prompt
posa una barra de progres de las tasques
Code edit (1 edits merged)
Please save this source code
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var CablesTask = Container.expand(function (x, y, id) {
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;
// 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));
self.isNearPlayer = distance < 120;
}
// 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));
if (distance < 120 && !self.isCompleted) {
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 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
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) {
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;
// 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));
self.isNearPlayer = distance < 100;
}
// 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));
if (distance < 100 && !self.isCompleted) {
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 reportButton = null;
var reportTarget = null;
var taskButton = null;
var currentTaskTarget = 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);
// Camera system variables
var cameras = [];
var securityRoom = null;
// 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);
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
var topWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
x: 1024,
y: 200
}));
walls.push(topWall);
var bottomWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
x: 1024,
y: 2500
}));
walls.push(bottomWall);
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 10,
x: 200,
y: 1366
}));
walls.push(leftWall);
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 10,
x: 1800,
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);
// Add spaceship floor tiles
for (var floorX = 300; floorX < 1800; floorX += 100) {
for (var floorY = 300; floorY < 2400; floorY += 100) {
var 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);
}
}
// 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 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);
sabotageButton.down = function (x, y, obj) {
if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing') {
performSabotage();
hideSabotageButton();
}
};
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();
}
};
// 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 = [
// 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'
}];
// Create tasks with better distribution
var taskTypeCount = {};
taskTypes.forEach(function (type) {
taskTypeCount[type] = 0;
});
for (var i = 0; i < totalTasks; i++) {
// Distribute tasks across different areas
var area = taskAreas[i % 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
var taskType = area.preferredType;
if (taskTypeCount[taskType] >= 4) {
// Too many of this type, pick a different one
var availableTypes = taskTypes.filter(function (type) {
return taskTypeCount[type] < 4;
});
if (availableTypes.length > 0) {
taskType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
}
}
taskTypeCount[taskType]++;
var task;
if (taskType === 'cables') {
task = new CablesTask(taskX, taskY, i);
} else {
task = new Task(taskX, taskY, i, taskType);
}
tasks.push(task);
game.addChild(task);
}
}
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 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') {
// Play sabotage sound
LK.getSound('sabotage').play();
// Create sabotage effects
var sabotageEffects = [function () {
// Power outage - flash screen black
LK.effects.flashScreen(0x000000, 3000);
phaseText.setText('Power Outage! Find the reactor!');
}, function () {
// Reactor meltdown - flash screen red
LK.effects.flashScreen(0xff0000, 2000);
phaseText.setText('Reactor Critical! Fix it now!');
}, function () {
// Oxygen depletion - flash screen blue
LK.effects.flashScreen(0x0000ff, 2500);
phaseText.setText('Oxygen Depleted! Restore life support!');
}];
// Execute random sabotage
var randomSabotage = sabotageEffects[Math.floor(Math.random() * sabotageEffects.length)];
randomSabotage();
// Reset phase text after delay
LK.setTimeout(function () {
if (gamePhase === 'playing') {
phaseText.setText('Complete Tasks or Find Impostors!');
}
}, 4000);
}
}
// 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 = 400 + Math.random() * 1200;
var y = 400 + Math.random() * 1800;
// 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;
}
}
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
var nearbyTasks = tasks.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
var incompleteTasks = tasks.filter(function (task) {
return !task.isCompleted;
});
if (incompleteTasks.length > 0) {
// Find closest 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 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(400 + Math.random() * 1200, 400 + Math.random() * 1800);
}
// 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 game
createWalls();
initializeTasks();
initializeCameras();
// Don't initialize players yet - wait for role selection
// Game controls
game.down = function (x, y, obj) {
if (gamePhase === 'playing' && currentPlayer && currentPlayer.isAlive) {
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();
}
}
// 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();
}
}
// 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 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 = tasks.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();
}
}
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -300,8 +300,81 @@
}
};
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 Player = Container.expand(function (isImpostor, color, id) {
var self = Container.call(this);
self.isImpostor = isImpostor || false;
self.playerColor = color || 0x00ff00;
@@ -388,8 +461,241 @@
}
};
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) {
var self = Container.call(this);
self.taskId = id || 0;
self.isCompleted = false;
@@ -710,8 +1016,11 @@
currentTaskText.anchor.set(0.5, 1);
currentTaskText.y = -50;
currentTaskText.alpha = 0;
LK.gui.center.addChild(currentTaskText);
+// Camera system variables
+var cameras = [];
+var securityRoom = null;
// Progress Bar Elements
var progressBarBackground = LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
@@ -919,17 +1228,33 @@
x: 1650,
y: 1800
}));
walls.push(engineRightWall);
- // Security room walls
- var securityWall = game.addChild(LK.getAsset('wall', {
+ // Security room walls - expanded for camera monitoring
+ var securityWallTop = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
- scaleX: 1.5,
+ scaleX: 3.0,
x: 1024,
- y: 800
+ y: 450
}));
- walls.push(securityWall);
+ 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,
@@ -1870,11 +2195,61 @@
}
}
});
}
+// 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 game
createWalls();
initializeTasks();
+initializeCameras();
// Don't initialize players yet - wait for role selection
// Game controls
game.down = function (x, y, obj) {
if (gamePhase === 'playing' && currentPlayer && currentPlayer.isAlive) {
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