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 door is open, it doesn't block movement
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 Ghost = Container.expand(function (deadPlayer) {
var self = Container.call(this);
self.x = deadPlayer.x;
self.y = deadPlayer.y;
self.targetX = deadPlayer.x;
self.targetY = deadPlayer.y;
self.speed = 1.5; // Slower than living players
self.deadPlayerId = deadPlayer.playerId;
// Create ghost graphics based on the dead player
var ghostGraphics = self.attachAsset(deadPlayer.isImpostor ? 'impostor' : 'crewmate', {
anchorX: 0.5,
anchorY: 0.5
});
ghostGraphics.tint = 0xAAAAFF; // Ghostly blue-white tint
ghostGraphics.alpha = 0.6; // Semi-transparent
// Add floating animation
self.floatOffset = 0;
self.floatSpeed = 0.05;
// Ghost moves randomly around the ship
self.wanderTimer = 0;
self.wanderInterval = 3000; // Change direction every 3 seconds
self.update = function () {
// Floating animation
self.floatOffset += self.floatSpeed;
ghostGraphics.y = Math.sin(self.floatOffset) * 5;
// Pulsing alpha effect
if (LK.ticks % 120 < 60) {
ghostGraphics.alpha = 0.6;
} else {
ghostGraphics.alpha = 0.3;
}
// Wandering behavior
self.wanderTimer += 16.67;
if (self.wanderTimer >= self.wanderInterval) {
self.wanderTimer = 0;
// Pick a random location to wander to
self.targetX = 300 + Math.random() * 1400;
self.targetY = 300 + Math.random() * 2000;
}
// Move towards target (ghosts can pass through walls)
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Phase in and out randomly
if (Math.random() < 0.01) {
tween(ghostGraphics, {
alpha: 0.1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onFinish() {
tween(ghostGraphics, {
alpha: 0.6,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500
});
}
});
}
};
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();
// Create ghost effect with dramatic animation
tween(self, {
alpha: 0.1,
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFFFFF
}, {
duration: 1000,
onFinish: function onFinish() {
// Create ghost entity
var ghost = new Ghost(self);
ghosts.push(ghost);
game.addChild(ghost);
// Ghost spawn effect
ghost.alpha = 0;
tween(ghost, {
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(ghost, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400
});
}
});
}
});
}
};
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 Sewer = Container.expand(function (x, y, id, targetSewerId) {
var self = Container.call(this);
self.sewerId = id || 0;
self.targetSewerId = targetSewerId || 0;
self.x = x;
self.y = y;
self.isNearPlayer = false;
self.lastNearPlayer = false;
self.teleportCooldown = 0;
self.teleportCooldownTime = 2000; // 2 seconds
// Sewer entrance graphics
var sewerGraphics = self.attachAsset('sewerEntrance', {
anchorX: 0.5,
anchorY: 0.5
});
sewerGraphics.tint = 0x654321; // Brown color for sewer
// Sewer name text
var sewerNameText = new Text2('SEWER ' + (self.sewerId + 1), {
size: 18,
fill: 0xFFFFFF
});
sewerNameText.anchor.set(0.5, 0);
sewerNameText.y = -50;
self.addChild(sewerNameText);
// Interaction hint text
var interactionText = new Text2('Enter Sewer (Impostors Only)', {
size: 16,
fill: 0xFF0000
});
interactionText.anchor.set(0.5, 0);
interactionText.y = 50;
interactionText.alpha = 0;
self.addChild(interactionText);
// Teleport cooldown text
var cooldownText = new Text2('', {
size: 14,
fill: 0xFF0000
});
cooldownText.anchor.set(0.5, 0);
cooldownText.y = 70;
cooldownText.alpha = 0;
self.addChild(cooldownText);
self.teleportPlayer = function (player) {
if (self.teleportCooldown > 0) return false;
// Find target sewer
var targetSewer = null;
for (var i = 0; i < sewers.length; i++) {
if (sewers[i].sewerId === self.targetSewerId) {
targetSewer = sewers[i];
break;
}
}
if (targetSewer) {
// Play teleport sound
LK.getSound('teleport').play();
// Teleport player to target sewer
player.x = targetSewer.x;
player.y = targetSewer.y;
player.targetX = targetSewer.x;
player.targetY = targetSewer.y;
// Set cooldown for both sewers
self.teleportCooldown = self.teleportCooldownTime;
targetSewer.teleportCooldown = targetSewer.teleportCooldownTime;
// Flash effect on both sewers
tween(sewerGraphics, {
tint: 0x00FFFF,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
onFinish: function onFinish() {
tween(sewerGraphics, {
tint: 0x654321,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
if (targetSewer.children && targetSewer.children.length > 0) {
var targetGraphics = targetSewer.children[0];
tween(targetGraphics, {
tint: 0x00FFFF,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
onFinish: function onFinish() {
tween(targetGraphics, {
tint: 0x654321,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
}
return true;
}
return false;
};
self.update = function () {
// Update teleport cooldown
if (self.teleportCooldown > 0) {
self.teleportCooldown -= 16.67; // Roughly 1 frame at 60fps
var secondsLeft = Math.ceil(self.teleportCooldown / 1000);
cooldownText.setText('Cooldown: ' + secondsLeft + 's');
cooldownText.alpha = 1;
} else {
cooldownText.alpha = 0;
}
// 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
});
}
// Pulsing animation for sewer entrance
if (LK.ticks % 120 < 60) {
sewerGraphics.alpha = 1.0;
} else {
sewerGraphics.alpha = 0.8;
}
};
self.down = function (x, y, obj) {
if (currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor && gamePhase === 'playing') {
var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2));
if (distance < 100 && self.teleportCooldown <= 0) {
self.teleportPlayer(currentPlayer);
}
}
};
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 taskCategories = {
critical: [],
// High priority tasks
normal: [],
// Standard tasks
maintenance: [] // Lower priority tasks
};
var taskDifficulty = {
electrical: 3,
cables: 4,
reactor: 5,
navigation: 2,
medical: 3,
engine: 4
};
var currentPlayer = null;
var totalTasks = 0; // Will be calculated based on actual crew members
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 ghosts = []; // Array to track ghost entities
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;
// Sewer system variables
var sewers = [];
// 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.5,
x: 1024,
y: 450
}));
walls.push(securityWallTop);
var securityWallLeft = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 2.5,
x: 700,
y: 600
}));
walls.push(securityWallLeft);
var securityWallRight = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 2.5,
x: 1350,
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 (var floorY = 200; floorY < 2550; floorY += 100) {
var floorTile = game.addChild(LK.getAsset('shipFloor', {
anchorX: 0.5,
anchorY: 0.5,
x: floorX,
y: floorY
}));
game.setChildIndex(floorTile, 0);
}
};
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 room separator walls to create distinct areas
var labSeparatorWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 1.5,
x: 375,
y: 850
}));
walls.push(labSeparatorWall);
var weaponsSeparatorWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 1.5,
x: 1675,
y: 850
}));
walls.push(weaponsSeparatorWall);
var medicalSeparatorWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
x: 1125,
y: 1300
}));
walls.push(medicalSeparatorWall);
var reactorSeparatorWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
x: 925,
y: 1300
}));
walls.push(reactorSeparatorWall);
// 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);
// Create kill cooldown timer (initially hidden)
var killCooldownTimer = new Text2('', {
size: 32,
fill: 0xFF0000
});
killCooldownTimer.anchor.set(0.5, 0.5);
killCooldownTimer.x = 0;
killCooldownTimer.y = 80;
killCooldownTimer.alpha = 0;
killCooldownTimer.visible = false;
LK.gui.center.addChild(killCooldownTimer);
killButton.down = function (x, y, obj) {
if (killTarget && currentPlayer && currentPlayer.isImpostor) {
if (currentPlayer.canKill()) {
if (currentPlayer.kill(killTarget)) {
updatePlayerCount();
hideKillButton();
// Hide cooldown timer when kill is successful
if (killCooldownTimer) {
killCooldownTimer.alpha = 0;
killCooldownTimer.visible = false;
}
}
} else {
// Show cooldown timer with animation
var cooldownLeft = Math.ceil((currentPlayer.killCooldown - (LK.ticks - currentPlayer.lastKillTime)) / 1000);
if (killCooldownTimer) {
killCooldownTimer.setText(cooldownLeft + 's');
killCooldownTimer.alpha = 1;
killCooldownTimer.visible = true;
// Pulsing animation for cooldown timer
tween(killCooldownTimer, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFF4444
}, {
duration: 300,
onFinish: function onFinish() {
tween(killCooldownTimer, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFF0000
}, {
duration: 300
});
}
});
}
if (taskCompletionText) {
taskCompletionText.setText('Kill cooldown: ' + cooldownLeft + 's');
taskCompletionText.alpha = 1;
taskCompletionText.y = 0;
tween(taskCompletionText, {
y: -100,
alpha: 0
}, {
duration: 2000
});
}
}
}
};
// 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);
// Make doorButtonText globally accessible
doorButton.doorButtonText = doorButtonText;
doorButton.down = function (x, y, obj) {
if (currentDoorTarget && currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor && gamePhase === 'playing') {
if (currentDoorTarget.isOpen) {
currentDoorTarget.close();
} else {
currentDoorTarget.open();
}
}
};
// 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) {
// Use stored area name if available
if (task.areaName) {
return task.areaName;
}
// Fallback to position-based naming
if (task.x < 500) {
if (task.y < 800) return 'Upper Left Wing';
if (task.y > 1800) return 'Lower Left Wing';
return 'Left Wing';
} else if (task.x > 1500) {
if (task.y < 800) return 'Upper Right Wing';
if (task.y > 1800) return 'Lower Right Wing';
return 'Right Wing';
} else {
if (task.y < 800) return 'Command Bridge';
if (task.y > 1800) return 'Cargo Bay';
if (task.y < 1200) return 'Security Area';
return 'Central Corridor';
}
}
// 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);
initializeTasks();
// Update total tasks based on actual crew members
var crewCount = players.filter(function (p) {
return !p.isImpostor;
}).length;
totalTasks = crewCount * 4;
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 - Navigation task
{
x: 900,
y: 350,
width: 250,
height: 150,
preferredType: 'navigation',
name: 'Command Bridge'
},
// Communication Array - Electrical task
{
x: 1500,
y: 525,
width: 200,
height: 125,
preferredType: 'electrical',
name: 'Communications'
},
// Laboratory - Medical task
{
x: 250,
y: 1000,
width: 200,
height: 200,
preferredType: 'medical',
name: 'Laboratory'
},
// Weapons Room - Reactor task
{
x: 1750,
y: 1000,
width: 200,
height: 200,
preferredType: 'reactor',
name: 'Weapons'
},
// Maintenance Tunnels - Cables task
{
x: 350,
y: 1925,
width: 200,
height: 125,
preferredType: 'cables',
name: 'Maintenance'
},
// Cargo Bay - Engine task
{
x: 900,
y: 2350,
width: 400,
height: 200,
preferredType: 'engine',
name: 'Cargo Bay'
},
// Upper Electrical room
{
x: 400,
y: 500,
width: 200,
height: 150,
preferredType: 'electrical',
name: 'Upper Electrical'
},
// Upper Engine room
{
x: 1200,
y: 500,
width: 200,
height: 150,
preferredType: 'engine',
name: 'Upper Engines'
},
// Security room
{
x: 800,
y: 800,
width: 300,
height: 200,
preferredType: 'navigation',
name: 'Security'
},
// Medical bay
{
x: 1200,
y: 1200,
width: 250,
height: 180,
preferredType: 'medical',
name: 'Medbay'
},
// Reactor room
{
x: 400,
y: 1200,
width: 250,
height: 180,
preferredType: 'reactor',
name: 'Reactor'
},
// Lower Engine room
{
x: 1200,
y: 2000,
width: 200,
height: 150,
preferredType: 'engine',
name: 'Lower Engines'
},
// Lower Electrical
{
x: 400,
y: 2000,
width: 200,
height: 150,
preferredType: 'electrical',
name: 'Lower Electrical'
},
// Central corridor
{
x: 800,
y: 1600,
width: 400,
height: 200,
preferredType: 'navigation',
name: 'Central Hub'
},
// Cables maintenance room
{
x: 600,
y: 1000,
width: 200,
height: 150,
preferredType: 'cables',
name: 'Maintenance'
}];
// 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;
var allAssignedTasks = []; // Track all tasks globally
// Create exactly 5 tasks per crew member for balanced gameplay
var tasksPerPlayer = 5;
crewMembers.forEach(function (player, playerIndex) {
var playerTaskCount = {};
taskTypes.forEach(function (type) {
playerTaskCount[type] = 0;
});
// Initialize assigned tasks array for this player
if (!player.assignedTasks) {
player.assignedTasks = [];
}
// Assign tasks with improved distribution
for (var i = 0; i < tasksPerPlayer; i++) {
// Ensure tasks are spread across different areas
var areaIndex = (playerIndex * tasksPerPlayer + i) % taskAreas.length;
var area = taskAreas[areaIndex];
// Generate position within area with some randomness
var taskX = area.x + (Math.random() - 0.5) * area.width * 0.8;
var taskY = area.y + (Math.random() - 0.5) * area.height * 0.8;
// Ensure task is within valid game boundaries
taskX = Math.max(200, Math.min(1800, taskX));
taskY = Math.max(300, Math.min(2500, taskY));
// Smart task type selection
var taskType = area.preferredType;
// Ensure variety - max 2 of same type per player
if (playerTaskCount[taskType] >= 2) {
var availableTypes = taskTypes.filter(function (type) {
return playerTaskCount[type] < 2;
});
if (availableTypes.length > 0) {
taskType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
} else {
// If all types have 2, allow 1 more of the area's preferred type
taskType = area.preferredType;
}
}
playerTaskCount[taskType]++;
taskTypeCount[taskType]++;
var task;
// Create appropriate task type
if (taskType === 'cables') {
task = new CablesTask(taskX, taskY, taskId, player.playerId);
} else {
task = new Task(taskX, taskY, taskId, taskType, player.playerId);
}
// Store area information for location hints
task.areaName = area.name;
task.areaType = area.preferredType;
// Add task to player's assigned tasks
player.assignedTasks.push(task);
allAssignedTasks.push(task);
tasks.push(task);
game.addChild(task);
taskId++;
}
});
// Update global task count
totalTasks = allAssignedTasks.length;
// Show task assignment summary for debugging
console.log('Task Assignment Complete:');
console.log('- Total Players:', crewMembers.length);
console.log('- Tasks per Player:', tasksPerPlayer);
console.log('- Total Tasks:', totalTasks);
console.log('- Task Distribution:', taskTypeCount);
}
function updateTaskProgress() {
// Calculate actual total tasks based on crew members
var crewMembers = players.filter(function (p) {
return !p.isImpostor;
});
var actualTotalTasks = totalTasks; // Use the globally tracked total
taskProgressText.setText('Tasks: ' + completedTasks + '/' + actualTotalTasks);
// Update progress bar with smooth animation
var progressPercentage = Math.min(completedTasks / actualTotalTasks, 1.0);
tween(progressBarFill, {
scaleX: progressPercentage * 8
}, {
duration: 300
});
// Show task completion animation with more detail
if (taskCompletionText) {
var remainingTasks = actualTotalTasks - completedTasks;
taskCompletionText.setText('Task Completed! (' + completedTasks + '/' + actualTotalTasks + ') - ' + remainingTasks + ' remaining');
taskCompletionText.alpha = 1;
taskCompletionText.y = 0;
// Scale animation for emphasis
tween(taskCompletionText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(taskCompletionText, {
y: -100,
alpha: 0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 2000
});
}
});
}
// Enhanced progress bar flash with completion level indication
var flashColor = progressPercentage > 0.8 ? 0x00FF00 : progressPercentage > 0.5 ? 0xFFFF00 : 0xFF8800;
tween(progressBarFill, {
tint: flashColor
}, {
duration: 200,
onFinish: function onFinish() {
tween(progressBarFill, {
tint: 0x00FF00
}, {
duration: 200
});
}
});
// Check win condition with percentage threshold
if (completedTasks >= actualTotalTasks) {
// Play victory sound effect
LK.getSound('taskComplete').play();
// Add victory message
if (taskCompletionText) {
taskCompletionText.setText('ALL TASKS COMPLETED! CREW WINS!');
taskCompletionText.tint = 0x00FF00;
taskCompletionText.alpha = 1;
taskCompletionText.y = 0;
taskCompletionText.scaleX = 1.5;
taskCompletionText.scaleY = 1.5;
}
endGame('crew');
} else {
// Show milestone celebrations
var milestones = [0.25, 0.5, 0.75];
milestones.forEach(function (milestone) {
if (Math.abs(progressPercentage - milestone) < 0.01) {
var percentComplete = Math.round(milestone * 100);
if (taskCompletionText) {
taskCompletionText.setText(percentComplete + '% of tasks completed! Keep going!');
taskCompletionText.tint = 0xFFFF00;
}
}
});
}
}
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;
}
// Also hide cooldown timer
if (killCooldownTimer) {
killCooldownTimer.alpha = 0;
killCooldownTimer.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 * (wall.scaleX || 1) / 2;
var wallRight = wall.x + wall.width * (wall.scaleX || 1) / 2;
var wallTop = wall.y - wall.height * (wall.scaleY || 1) / 2;
var wallBottom = wall.y + wall.height * (wall.scaleY || 1) / 2;
// Check if player rectangle intersects with wall rectangle
if (playerLeft < wallRight && playerRight > wallLeft && playerTop < wallBottom && playerBottom > wallTop) {
return true;
}
}
// Check door collisions - only check if door is closed
for (var i = 0; i < doors.length; i++) {
var door = doors[i];
// Only check collision if door is closed
if (!door.isOpen && 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 - optimized for performance
function updateAI() {
// Process only a subset of players each update to spread load
var playersToUpdate = players.length;
var startIndex = LK.ticks % playersToUpdate;
for (var playerIndex = 0; playerIndex < Math.min(2, playersToUpdate); playerIndex++) {
var index = (startIndex + playerIndex) % playersToUpdate;
var player = players[index];
if (player === currentPlayer || !player.isAlive) continue;
// Crew member AI - simplified for performance
if (!player.isImpostor && gamePhase === 'playing') {
// Priority 1: Fix sabotages (highest priority)
if (isSabotageActive && sabotageIndicators.length > 0) {
// Check if near any sabotage indicator (simplified)
var nearIndicator = false;
for (var i = 0; i < sabotageIndicators.length; i++) {
var indicator = sabotageIndicators[i];
var dx = indicator.x - player.x;
var dy = indicator.y - player.y;
if (dx * dx + dy * dy < 10000) {
// 100 * 100
nearIndicator = true;
if (Math.random() < 0.1) {
// Reduced chance
fixSabotage();
return;
}
break;
}
}
if (!nearIndicator && Math.random() < 0.05) {
// Move towards first sabotage indicator
var indicator = sabotageIndicators[0];
player.moveTo(indicator.x + (Math.random() - 0.5) * 30, indicator.y + (Math.random() - 0.5) * 30);
}
continue;
}
// Priority 2: Report dead bodies (simplified)
var foundDeadBody = false;
for (var i = 0; i < players.length; i++) {
var p = players[i];
if (p === player || p.isAlive) continue;
var dx = p.x - player.x;
var dy = p.y - player.y;
if (dx * dx + dy * dy < 22500) {
// 150 * 150
foundDeadBody = true;
if (Math.random() < 0.05) {
// Reduced chance
reportDeadBody();
return;
}
break;
}
}
if (foundDeadBody) continue;
// Priority 3: Complete assigned tasks (simplified)
if (player.assignedTasks && player.assignedTasks.length > 0) {
var nearTask = false;
for (var i = 0; i < player.assignedTasks.length; i++) {
var task = player.assignedTasks[i];
if (task.isCompleted) continue;
var dx = task.x - player.x;
var dy = task.y - player.y;
if (dx * dx + dy * dy < 10000) {
// 100 * 100
nearTask = true;
if (Math.random() < 0.1) {
task.completionProgress++;
if (task.completionProgress >= task.requiredClicks) {
task.complete();
player.completeTask();
completedTasks++;
updateTaskProgress();
}
}
break;
}
}
// Move towards task occasionally
if (!nearTask && Math.random() < 0.02) {
var incompleteTasks = player.assignedTasks.filter(function (task) {
return !task.isCompleted;
});
if (incompleteTasks.length > 0) {
var task = incompleteTasks[0];
player.moveTo(task.x + (Math.random() - 0.5) * 50, task.y + (Math.random() - 0.5) * 50);
}
}
}
}
// Impostor AI (simplified)
if (player.isImpostor && gamePhase === 'playing') {
// Try to kill nearby crew members
var foundTarget = false;
for (var i = 0; i < players.length; i++) {
var p = players[i];
if (p === player || !p.isAlive || p.isImpostor) continue;
var dx = p.x - player.x;
var dy = p.y - player.y;
if (dx * dx + dy * dy < 14400) {
// 120 * 120
foundTarget = true;
if (player.canKill() && Math.random() < 0.05) {
// Reduced chance
player.kill(p);
updatePlayerCount();
return;
}
break;
}
}
// Move occasionally
if (!foundTarget && Math.random() < 0.01) {
player.moveTo(300 + Math.random() * 1400, 300 + Math.random() * 2000);
}
}
}
}
// 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 sewers
function initializeSewers() {
// Create sewer entrances throughout the ship for room-to-room teleportation
var sewerPositions = [
// Command Bridge sewer
{
x: 950,
y: 350,
id: 0,
targetId: 8
},
// Communication Array sewer
{
x: 1550,
y: 450,
id: 1,
targetId: 9
},
// Security Room sewer
{
x: 1024,
y: 550,
id: 2,
targetId: 10
},
// Laboratory sewer
{
x: 200,
y: 1000,
id: 3,
targetId: 11
},
// Weapons Room sewer
{
x: 1850,
y: 1000,
id: 4,
targetId: 3
},
// Medical Bay sewer
{
x: 1300,
y: 1400,
id: 5,
targetId: 6
},
// Reactor Room sewer
{
x: 750,
y: 1400,
id: 6,
targetId: 5
},
// Central Corridor sewer
{
x: 1024,
y: 1366,
id: 7,
targetId: 12
},
// Maintenance Tunnels sewer
{
x: 300,
y: 1925,
id: 8,
targetId: 0
},
// Upper Left Electrical sewer
{
x: 350,
y: 650,
id: 9,
targetId: 1
},
// Lower Left Electrical sewer
{
x: 350,
y: 2100,
id: 10,
targetId: 2
},
// Upper Right Engine sewer
{
x: 1700,
y: 650,
id: 11,
targetId: 4
},
// Cargo Bay sewer
{
x: 1024,
y: 2450,
id: 12,
targetId: 7
},
// Storage Left sewer
{
x: 250,
y: 1366,
id: 13,
targetId: 14
},
// Storage Right sewer
{
x: 1800,
y: 1366,
id: 14,
targetId: 13
},
// Lower Engine Left sewer
{
x: 400,
y: 1800,
id: 15,
targetId: 16
},
// Lower Engine Right sewer
{
x: 1650,
y: 1800,
id: 16,
targetId: 15
}];
for (var i = 0; i < sewerPositions.length; i++) {
var pos = sewerPositions[i];
var sewer = new Sewer(pos.x, pos.y, pos.id, pos.targetId);
sewers.push(sewer);
game.addChild(sewer);
}
}
// Initialize doors
function initializeDoors() {
// Create doors to define proper room entrances and corridors
var doorPositions = [
// Command Bridge room doors
{
x: 850,
y: 450,
orientation: 0
},
// Bridge entrance left
{
x: 1200,
y: 450,
orientation: 0
},
// Bridge entrance right
// Communication Array room doors
{
x: 1400,
y: 525,
orientation: 1
},
// Comm room entrance
// Security room doors
{
x: 800,
y: 600,
orientation: 1
},
// Security entrance left
{
x: 1248,
y: 600,
orientation: 1
},
// Security entrance right
// Laboratory room doors
{
x: 450,
y: 900,
orientation: 0
},
// Lab entrance top
{
x: 450,
y: 1100,
orientation: 0
},
// Lab entrance bottom
// Weapons room doors
{
x: 1600,
y: 900,
orientation: 0
},
// Weapons entrance top
{
x: 1600,
y: 1100,
orientation: 0
},
// Weapons entrance bottom
// Central corridor access doors
{
x: 700,
y: 1000,
orientation: 1
},
// Left corridor access
{
x: 1350,
y: 1000,
orientation: 1
},
// Right corridor access
// Main corridor horizontal doors
{
x: 824,
y: 1300,
orientation: 0
},
// Central access left
{
x: 1224,
y: 1300,
orientation: 0
},
// Central access right
// Medical bay and reactor room doors
{
x: 1200,
y: 1400,
orientation: 0
},
// Medical entrance
{
x: 850,
y: 1400,
orientation: 0
},
// Reactor entrance
// Maintenance tunnels doors
{
x: 600,
y: 1850,
orientation: 0
},
// Maintenance entrance
// Lower corridor doors
{
x: 700,
y: 1900,
orientation: 1
},
// Lower left access
{
x: 1350,
y: 1900,
orientation: 1
},
// Lower right access
// Cargo bay doors
{
x: 800,
y: 2200,
orientation: 0
},
// Cargo entrance left
{
x: 1248,
y: 2200,
orientation: 0
},
// Cargo entrance right
// Engine room doors
{
x: 500,
y: 1700,
orientation: 1
},
// Left engine entrance
{
x: 1550,
y: 1700,
orientation: 1
},
// Right engine entrance
// Additional room separators
{
x: 1024,
y: 750,
orientation: 0
},
// Upper central passage
{
x: 1024,
y: 1550,
orientation: 0
},
// Lower central passage
// Storage room doors
{
x: 350,
y: 1200,
orientation: 1
},
// Left storage
{
x: 1700,
y: 1200,
orientation: 1
},
// Right storage
// Electrical room doors
{
x: 500,
y: 600,
orientation: 1
},
// Upper electrical
{
x: 500,
y: 1950,
orientation: 1
} // Lower electrical
];
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);
}
}
// Common task areas that all players should have access to
var commonTaskAreas = [{
x: 1024,
y: 1366,
// Central emergency button area
taskType: 'emergency',
name: 'Emergency Protocols'
}, {
x: 1024,
y: 600,
// Security area
taskType: 'security',
name: 'Security Systems'
}];
// Initialize game
createWalls();
initializeCameras();
initializeDoors();
initializeSewers();
// 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 * (wall.scaleX || 1) / 2;
var wallRight = wall.x + wall.width * (wall.scaleX || 1) / 2;
var wallTop = wall.y - wall.height * (wall.scaleY || 1) / 2;
var wallBottom = wall.y + wall.height * (wall.scaleY || 1) / 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 - optimized for performance
function updateVisibility() {
if (!currentPlayer || !currentPlayer.isAlive) return;
var playerX = currentPlayer.x;
var playerY = currentPlayer.y;
var visionRange = 300; // How far player can see
var visionRangeSquared = visionRange * visionRange; // Avoid sqrt when possible
// Update player visibility
for (var i = 0; i < players.length; i++) {
var player = players[i];
if (player === currentPlayer) {
player.alpha = 1.0; // Always see yourself
continue;
}
// Use squared distance to avoid expensive sqrt
var dx = player.x - playerX;
var dy = player.y - playerY;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= visionRangeSquared) {
player.alpha = 1.0; // Fully visible (simplified - no line of sight for performance)
} else {
player.alpha = 0.0; // Hidden
}
}
// Update task visibility (only for crew members) - simplified
if (!currentPlayer.isImpostor) {
for (var i = 0; i < tasks.length; i++) {
var task = tasks[i];
var dx = task.x - playerX;
var dy = task.y - playerY;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= visionRangeSquared) {
task.alpha = task.isCompleted ? 0.5 : 1.0; // Visible
} else {
task.alpha = 0.0; // Hidden
}
}
}
// Update other objects with simple distance check (no line of sight for performance)
// Update camera visibility
for (var i = 0; i < cameras.length; i++) {
var camera = cameras[i];
var dx = camera.x - playerX;
var dy = camera.y - playerY;
var distanceSquared = dx * dx + dy * dy;
camera.alpha = distanceSquared <= visionRangeSquared ? 1.0 : 0.0;
}
// Update door visibility
for (var i = 0; i < doors.length; i++) {
var door = doors[i];
var dx = door.x - playerX;
var dy = door.y - playerY;
var distanceSquared = dx * dx + dy * dy;
door.alpha = distanceSquared <= visionRangeSquared ? 1.0 : 0.0;
}
// Update sabotage indicator visibility
for (var i = 0; i < sabotageIndicators.length; i++) {
var indicator = sabotageIndicators[i];
var dx = indicator.x - playerX;
var dy = indicator.y - playerY;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= visionRangeSquared) {
// Preserve existing alpha for animations if visible
} else {
indicator.alpha = 0.0; // Hidden
}
}
// Update sewer visibility
for (var i = 0; i < sewers.length; i++) {
var sewer = sewers[i];
var dx = sewer.x - playerX;
var dy = sewer.y - playerY;
var distanceSquared = dx * dx + dy * dy;
sewer.alpha = distanceSquared <= visionRangeSquared ? 1.0 : 0.0;
}
// Update ghost visibility - ghosts are always somewhat visible but fade with distance
for (var i = 0; i < ghosts.length; i++) {
var ghost = ghosts[i];
var dx = ghost.x - playerX;
var dy = ghost.y - playerY;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= visionRangeSquared) {
// Ghosts maintain their alpha but are visible
ghost.visible = true;
} else {
// Ghosts fade out with distance but remain slightly visible
ghost.alpha = Math.max(ghost.alpha * 0.3, 0.1);
}
}
}
// 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 less frequently for performance
if (LK.ticks % 6 === 0) {
// Update visibility 10 times per second instead of 60
updateVisibility();
}
// Update ghosts
for (var i = 0; i < ghosts.length; i++) {
var ghost = ghosts[i];
if (ghost && ghost.update) {
ghost.update();
}
}
}
// Update cooldowns
if (meetingCooldown > 0) {
meetingCooldown -= 16.67; // Roughly 1 frame at 60fps
}
// Update AI less frequently for performance
if (LK.ticks % 60 === 0) {
// Update AI once per second instead of twice per second
updateAI();
}
// Update current player impostor kill opportunities - optimized
if (currentPlayer && currentPlayer.isImpostor && gamePhase === 'playing') {
var nearestTarget = null;
var nearestDistanceSquared = 14400; // 120 * 120
// Find nearest target with optimized distance calculation
for (var i = 0; i < players.length; i++) {
var p = players[i];
if (p === currentPlayer || !p.isAlive || p.isImpostor) continue;
var dx = p.x - currentPlayer.x;
var dy = p.y - currentPlayer.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared < nearestDistanceSquared) {
nearestTarget = p;
nearestDistanceSquared = distanceSquared;
}
}
// Show kill button if there are targets nearby (regardless of cooldown)
if (nearestTarget) {
// Add visual indicator for kill opportunity
if (LK.ticks % 30 < 15) {
nearestTarget.alpha = 0.7;
} else {
nearestTarget.alpha = 1.0;
}
// Show kill button for closest target
if (!killButton.visible) {
showKillButton(nearestTarget);
}
// Update kill button opacity based on cooldown
if (currentPlayer.canKill()) {
killButton.alpha = 1.0;
// Hide cooldown timer when ready to kill
if (killCooldownTimer) {
killCooldownTimer.alpha = 0;
killCooldownTimer.visible = false;
}
} else {
killButton.alpha = 0.5; // Show button but dimmed during cooldown
// Show and update cooldown timer
if (killCooldownTimer) {
var cooldownLeft = Math.ceil((currentPlayer.killCooldown - (LK.ticks - currentPlayer.lastKillTime)) / 1000);
killCooldownTimer.setText(cooldownLeft + 's');
killCooldownTimer.alpha = 0.9;
killCooldownTimer.visible = true;
// Gentle pulsing effect during cooldown
if (LK.ticks % 60 < 30) {
killCooldownTimer.alpha = 0.9;
} else {
killCooldownTimer.alpha = 0.6;
}
}
}
} else {
// Hide kill button if no targets nearby
if (killButton.visible) {
hideKillButton();
}
// Reset target alphas
for (var i = 0; i < players.length; i++) {
var p = players[i];
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 - optimized
if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') {
// Door button helper functions
var showDoorButton = function showDoorButton(door) {
if (doorButton && currentPlayer && currentPlayer.isImpostor) {
currentDoorTarget = door;
doorButton.alpha = 1;
doorButton.visible = true;
// Update button text based on door state
if (door.isOpen) {
doorButton.doorButtonText.setText('CLOSE');
} else {
doorButton.doorButtonText.setText('OPEN');
}
// 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
});
}
});
}
};
var _hideDoorButton = function _hideDoorButton() {
if (doorButton) {
currentDoorTarget = null;
doorButton.alpha = 0;
doorButton.visible = false;
}
}; // Update door button for impostors near doors (only if no sabotage active)
var nearestDeadBody = null;
var nearestDeadDistanceSquared = 22500; // 150 * 150
// Find nearest dead body with optimized distance calculation
for (var i = 0; i < players.length; i++) {
var p = players[i];
if (p === currentPlayer || p.isAlive) continue;
var dx = p.x - currentPlayer.x;
var dy = p.y - currentPlayer.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared < nearestDeadDistanceSquared) {
nearestDeadBody = p;
nearestDeadDistanceSquared = distanceSquared;
}
}
// Show report button if there are dead bodies nearby
if (nearestDeadBody) {
// Add visual indicator for dead body
if (LK.ticks % 60 < 30) {
nearestDeadBody.alpha = 0.5;
} else {
nearestDeadBody.alpha = 0.3;
}
// Show report button for closest dead body
if (!reportButton.visible) {
showReportButton(nearestDeadBody);
}
} 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 = Math.min(sabotageEffect.alpha * 1.2, 0.8);
} else {
sabotageEffect.alpha = Math.max(sabotageEffect.alpha * 0.8, 0.3);
}
}
}
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
@@ -1006,11 +1006,11 @@
sewerNameText.anchor.set(0.5, 0);
sewerNameText.y = -50;
self.addChild(sewerNameText);
// Interaction hint text
- var interactionText = new Text2('Enter Sewer', {
+ var interactionText = new Text2('Enter Sewer (Impostors Only)', {
size: 16,
- fill: 0x00FFFF
+ fill: 0xFF0000
});
interactionText.anchor.set(0.5, 0);
interactionText.y = 50;
interactionText.alpha = 0;
@@ -1097,9 +1097,9 @@
}
// Check if player is near
self.lastNearPlayer = self.isNearPlayer;
self.isNearPlayer = false;
- if (currentPlayer && currentPlayer.isAlive) {
+ 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
@@ -1128,9 +1128,9 @@
sewerGraphics.alpha = 0.8;
}
};
self.down = function (x, y, obj) {
- if (currentPlayer && currentPlayer.isAlive && gamePhase === 'playing') {
+ if (currentPlayer && currentPlayer.isAlive && currentPlayer.isImpostor && gamePhase === 'playing') {
var distance = Math.sqrt(Math.pow(currentPlayer.x - self.x, 2) + Math.pow(currentPlayer.y - self.y, 2));
if (distance < 100 && self.teleportCooldown <= 0) {
self.teleportPlayer(currentPlayer);
}
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