/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ExitGate = Container.expand(function () {
var self = Container.call(this);
var exitGraphics = self.attachAsset('exitGate', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Generator = Container.expand(function () {
var self = Container.call(this);
var generatorGraphics = self.attachAsset('generator', {
anchorX: 0.5,
anchorY: 0.5
});
var indicatorGraphics = self.attachAsset('generatorIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
indicatorGraphics.y = -50;
var repairText = new Text2('0%', {
size: 30,
fill: '#ffffff'
});
repairText.anchor.set(0.5, 0.5);
repairText.y = 0;
self.addChild(repairText);
self.isRepaired = false;
self.repairProgress = 0;
self.maxRepairProgress = 100;
self.update = function () {
repairText.setText(Math.floor(self.repairProgress / 100 * 100) + '%');
if (self.isRepaired) {
generatorGraphics.tint = 0x00ff00;
}
};
return self;
});
var HidingSpot = Container.expand(function () {
var self = Container.call(this);
var hidingGraphics = self.attachAsset('hidingSpot', {
anchorX: 0.5,
anchorY: 0.5
});
self.isOccupied = false;
return self;
});
var Killer = Container.expand(function () {
var self = Container.call(this);
var killerGraphics = self.attachAsset('killer', {
anchorX: 0.5,
anchorY: 0.5
});
var killerEyeLeft = self.attachAsset('killerEyes', {
anchorX: 0.5,
anchorY: 0.5
});
killerEyeLeft.x = -12;
killerEyeLeft.y = -8;
var killerEyeRight = self.attachAsset('killerEyes', {
anchorX: 0.5,
anchorY: 0.5
});
killerEyeRight.x = 12;
killerEyeRight.y = -8;
self.speed = 3.2;
self.detectionRadius = 400;
self.aggressiveRadius = 180;
self.targetSurvivor = null;
self.isChasing = false;
self.chaseTimer = 0;
self.chaseDuration = 0;
self.lastX = self.x;
self.lastY = self.y;
self.huntingMode = false;
self.predictionX = 0;
self.predictionY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var Survivor = Container.expand(function () {
var self = Container.call(this);
var survivorGraphics = self.attachAsset('survivor', {
anchorX: 0.5,
anchorY: 0.5
});
var survivorHead = self.attachAsset('survivorHead', {
anchorX: 0.5,
anchorY: 0.5
});
survivorHead.y = -15;
self.speed = 3;
self.isRunning = false;
self.isHiding = false;
self.isRepairing = false;
self.repairProgress = 0;
self.maxRepairTime = 300;
self.trailCounter = 0;
self.lastX = self.x;
self.lastY = self.y;
self.id = 0;
self.escaped = false;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var survivors = [];
var activeSurvivor = 0;
var killer;
var generators = [];
var walls = [];
var hidingSpots = [];
var exitGates = [];
var generatorsRepaired = 0;
var gameState = 'playing'; // playing, chasing, won, lost, killerMode
var dragNode = null;
var survivorTrail = [];
var killerDetected = false;
var detectionFlashTimer = 0;
var gameMode = 'survivor'; // survivor or killer
var survivorsEscaped = 0;
// Initialize UI
var statusText = new Text2('Generators: 0/3', {
size: 80,
fill: '#ffffff'
});
statusText.anchor.set(0.5, 0);
LK.gui.top.addChild(statusText);
var objectiveText = new Text2('Repair all generators to escape', {
size: 60,
fill: '#ffffff'
});
objectiveText.anchor.set(0.5, 0.5);
objectiveText.y = 80;
LK.gui.center.addChild(objectiveText);
var gameModeText = new Text2('Mode: SURVIVOR', {
size: 50,
fill: '#00ff00'
});
gameModeText.anchor.set(0.5, 0);
gameModeText.x = 1024;
gameModeText.y = 150;
game.addChild(gameModeText);
// Game mode selection UI
var modeSelectionContainer = new Container();
modeSelectionContainer.x = 0;
modeSelectionContainer.y = 0;
game.addChild(modeSelectionContainer);
var titleText = new Text2('PIXEL DREAD', {
size: 120,
fill: '#ff0000'
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
modeSelectionContainer.addChild(titleText);
var selectText = new Text2('SELECT YOUR ROLE', {
size: 80,
fill: '#ffffff'
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 900;
modeSelectionContainer.addChild(selectText);
// Survivor button
var survivorButtonBg = LK.getAsset('hidingSpot', {
anchorX: 0.5,
anchorY: 0.5
});
survivorButtonBg.x = 600;
survivorButtonBg.y = 1400;
modeSelectionContainer.addChild(survivorButtonBg);
var survivorButtonText = new Text2('SURVIVOR\n(Repair & Escape)', {
size: 50,
fill: '#00ff00'
});
survivorButtonText.anchor.set(0.5, 0.5);
survivorButtonText.x = 600;
survivorButtonText.y = 1400;
modeSelectionContainer.addChild(survivorButtonText);
// Killer button
var killerButtonBg = LK.getAsset('killer', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
killerButtonBg.x = 1450;
killerButtonBg.y = 1400;
modeSelectionContainer.addChild(killerButtonBg);
var killerButtonText = new Text2('KILLER\n(Hunt Survivors)', {
size: 50,
fill: '#ff0000'
});
killerButtonText.anchor.set(0.5, 0.5);
killerButtonText.x = 1450;
killerButtonText.y = 1400;
modeSelectionContainer.addChild(killerButtonText);
var gameStarted = false;
var modeSelectionActive = true;
// Create arena layout
function initializeArena() {
// Create perimeter walls
var wallPositions = [{
x: 100,
y: 100
}, {
x: 300,
y: 100
}, {
x: 500,
y: 100
}, {
x: 700,
y: 100
}, {
x: 900,
y: 100
}, {
x: 1100,
y: 100
}, {
x: 1300,
y: 100
}, {
x: 1500,
y: 100
}, {
x: 1700,
y: 100
}, {
x: 1900,
y: 100
}, {
x: 100,
y: 2600
}, {
x: 300,
y: 2600
}, {
x: 500,
y: 2600
}, {
x: 700,
y: 2600
}, {
x: 900,
y: 2600
}, {
x: 1100,
y: 2600
}, {
x: 1300,
y: 2600
}, {
x: 1500,
y: 2600
}, {
x: 1700,
y: 2600
}, {
x: 1900,
y: 2600
}, {
x: 100,
y: 300
}, {
x: 100,
y: 600
}, {
x: 100,
y: 900
}, {
x: 100,
y: 1200
}, {
x: 100,
y: 1500
}, {
x: 100,
y: 1800
}, {
x: 100,
y: 2100
}, {
x: 100,
y: 2400
}, {
x: 1900,
y: 300
}, {
x: 1900,
y: 600
}, {
x: 1900,
y: 900
}, {
x: 1900,
y: 1200
}, {
x: 1900,
y: 1500
}, {
x: 1900,
y: 1800
}, {
x: 1900,
y: 2100
}, {
x: 1900,
y: 2400
}];
for (var i = 0; i < wallPositions.length; i++) {
var wall = game.addChild(new Wall());
wall.x = wallPositions[i].x;
wall.y = wallPositions[i].y;
walls.push(wall);
}
// Internal obstacles
var obstaclePositions = [{
x: 400,
y: 400
}, {
x: 1600,
y: 400
}, {
x: 400,
y: 2300
}, {
x: 1600,
y: 2300
}, {
x: 1024,
y: 800
}, {
x: 1024,
y: 1900
}, {
x: 600,
y: 1350
}, {
x: 1450,
y: 1350
}];
for (var i = 0; i < obstaclePositions.length; i++) {
var wall = game.addChild(new Wall());
wall.x = obstaclePositions[i].x;
wall.y = obstaclePositions[i].y;
walls.push(wall);
}
// Create hiding spots
var hidingPositions = [{
x: 300,
y: 600
}, {
x: 1700,
y: 600
}, {
x: 300,
y: 2100
}, {
x: 1700,
y: 2100
}, {
x: 800,
y: 1350
}, {
x: 1250,
y: 1350
}];
for (var i = 0; i < hidingPositions.length; i++) {
var hiding = game.addChild(new HidingSpot());
hiding.x = hidingPositions[i].x;
hiding.y = hidingPositions[i].y;
hidingSpots.push(hiding);
}
// Create generators
var generatorPositions = [{
x: 350,
y: 1350
}, {
x: 1700,
y: 800
}, {
x: 1300,
y: 2200
}];
for (var i = 0; i < generatorPositions.length; i++) {
var gen = game.addChild(new Generator());
gen.x = generatorPositions[i].x;
gen.y = generatorPositions[i].y;
generators.push(gen);
}
// Create exit gates (spawn but locked)
var exitPositions = [{
x: 200,
y: 1350
}, {
x: 1850,
y: 1350
}];
for (var i = 0; i < exitPositions.length; i++) {
var exit = game.addChild(new ExitGate());
exit.x = exitPositions[i].x;
exit.y = exitPositions[i].y;
exitGates.push(exit);
}
}
// Create multiple survivors
var survivorSpawns = [{
x: 300,
y: 500
}, {
x: 1750,
y: 500
}, {
x: 300,
y: 2200
}, {
x: 1750,
y: 2200
}];
for (var s = 0; s < survivorSpawns.length; s++) {
var survivor = game.addChild(new Survivor());
survivor.x = survivorSpawns[s].x;
survivor.y = survivorSpawns[s].y;
survivor.id = s;
survivor.escaped = false;
survivors.push(survivor);
}
// Create killer
killer = game.addChild(new Killer());
killer.x = 1024;
killer.y = 300;
killer.mode = gameMode;
// Initialize arena
initializeArena();
// Play ambient music
LK.playMusic('ambience');
// Collision detection helper
function checkCollision(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDist = obj1.width / 2 + obj2.width / 2;
return distance < minDist;
}
// Distance calculation
function getDistance(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Obstacle collision check
function isPathBlocked(fromX, fromY, toX, toY) {
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var dx = toX - fromX;
var dy = toY - fromY;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return false;
var stepX = dx / len * 5;
var stepY = dy / len * 5;
var steps = Math.floor(len / 5);
var checkX = fromX;
var checkY = fromY;
for (var s = 0; s < steps; s++) {
checkX += stepX;
checkY += stepY;
var wallDx = checkX - wall.x;
var wallDy = checkY - wall.y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 50) return true;
}
}
return false;
}
// Movement control system
var gameWidth = 2048;
var gameHeight = 2732;
var controlZoneSize = 300;
var currentTouchId = null;
var currentTouchX = 0;
var currentTouchY = 0;
var touchDirectionX = 0;
var touchDirectionY = 0;
game.down = function (x, y, obj) {
if (modeSelectionActive) {
// Handle mode selection
if (Math.abs(x - 600) < 150 && Math.abs(y - 1400) < 150) {
gameMode = 'survivor';
killer.mode = 'survivor';
modeSelectionActive = false;
gameStarted = true;
} else if (Math.abs(x - 1450) < 150 && Math.abs(y - 1400) < 150) {
gameMode = 'killer';
killer.mode = 'killer';
modeSelectionActive = false;
gameStarted = true;
}
return;
}
if (gameState !== 'playing' && gameState !== 'killerMode') return;
currentTouchId = 1;
currentTouchX = x;
currentTouchY = y;
};
game.move = function (x, y, obj) {
if (modeSelectionActive || gameState !== 'playing' && gameState !== 'killerMode') return;
if (currentTouchId === null) return;
currentTouchX = x;
currentTouchY = y;
var centerX = gameWidth / 2;
var centerY = gameHeight / 2;
var angle = Math.atan2(currentTouchY - centerY, currentTouchX - centerX);
var distance = Math.sqrt(Math.pow(currentTouchX - centerX, 2) + Math.pow(currentTouchY - centerY, 2));
var maxDistance = 600;
var effectiveDistance = Math.min(distance, maxDistance);
touchDirectionX = Math.cos(angle) * (effectiveDistance / maxDistance);
touchDirectionY = Math.sin(angle) * (effectiveDistance / maxDistance);
};
game.up = function (x, y, obj) {
if (modeSelectionActive) return;
currentTouchId = null;
touchDirectionX = 0;
touchDirectionY = 0;
};
// Main game update loop
game.update = function () {
// Handle mode selection screen
if (modeSelectionActive) {
statusText.visible = false;
objectiveText.visible = false;
gameModeText.visible = false;
return;
}
statusText.visible = true;
objectiveText.visible = true;
gameModeText.visible = true;
if (gameState !== 'playing' && gameState !== 'killerMode') return;
// Update all survivors
for (var s = 0; s < survivors.length; s++) {
survivors[s].update();
}
killer.update();
if (gameMode === 'survivor') {
// Survivor mode gameplay
var survivor = survivors[activeSurvivor];
// Apply directional touch-based movement
var moveX = touchDirectionX * survivor.speed;
var moveY = touchDirectionY * survivor.speed;
survivor.x += moveX;
survivor.y += moveY;
survivor.isRunning = Math.abs(touchDirectionX) > 0.1 || Math.abs(touchDirectionY) > 0.1;
var canMove = true;
for (var i = 0; i < walls.length; i++) {
if (getDistance({
x: survivor.x,
y: survivor.y
}, walls[i]) < 50) {
canMove = false;
survivor.x -= moveX;
survivor.y -= moveY;
break;
}
}
// Clamp survivor to arena
if (survivor.x < 150) survivor.x = 150;
if (survivor.x > 1900) survivor.x = 1900;
if (survivor.y < 150) survivor.y = 150;
if (survivor.y > 2600) survivor.y = 2600;
// Update killer AI with enhanced difficulty
var closestDist = 999999;
var closestSurvivor = null;
for (var s = 0; s < survivors.length; s++) {
if (!survivors[s].escaped) {
var dist = getDistance(killer, survivors[s]);
if (dist < closestDist) {
closestDist = dist;
closestSurvivor = survivors[s];
}
}
}
if (closestSurvivor) {
if (closestDist < killer.detectionRadius) {
killerDetected = true;
detectionFlashTimer = 30;
killer.targetSurvivor = closestSurvivor;
// Predictive movement for harder AI
var predictX = closestSurvivor.x;
var predictY = closestSurvivor.y;
if (closestSurvivor.isRunning) {
var distX = closestSurvivor.x - closestSurvivor.lastX;
var distY = closestSurvivor.y - closestSurvivor.lastY;
predictX += distX * 8;
predictY += distY * 8;
}
killer.predictionX = predictX;
killer.predictionY = predictY;
// Killer moves toward prediction point
if (closestDist > 30) {
var angle = Math.atan2(predictY - killer.y, predictX - killer.x);
var killerSpeed = closestDist < killer.aggressiveRadius ? killer.speed * 2 : killer.speed;
var killerNewX = killer.x + Math.cos(angle) * killerSpeed;
var killerNewY = killer.y + Math.sin(angle) * killerSpeed;
var canKillerMove = true;
for (var i = 0; i < walls.length; i++) {
if (getDistance({
x: killerNewX,
y: killerNewY
}, walls[i]) < 50) {
canKillerMove = false;
break;
}
}
if (canKillerMove) {
killer.x = killerNewX;
killer.y = killerNewY;
}
}
if (closestDist < 50) {
gameState = 'chasing';
LK.getSound('chase').play();
}
} else {
killerDetected = false;
}
}
// Update hiding
survivor.isHiding = false;
for (var i = 0; i < hidingSpots.length; i++) {
if (getDistance(survivor, hidingSpots[i]) < 45) {
survivor.isHiding = true;
break;
}
}
// Check generator repair
var currentRepairingGen = null;
for (var i = 0; i < generators.length; i++) {
var gen = generators[i];
if (!gen.isRepaired && getDistance(survivor, gen) < 60 && !survivor.isHiding && !survivor.isRunning) {
currentRepairingGen = gen;
break;
}
}
if (currentRepairingGen && !survivor.isRunning) {
survivor.isRepairing = true;
survivor.repairProgress += 1;
currentRepairingGen.repairProgress = survivor.repairProgress;
if (survivor.repairProgress >= 300) {
currentRepairingGen.isRepaired = true;
generatorsRepaired++;
survivor.isRepairing = false;
survivor.repairProgress = 0;
LK.getSound('repair').play();
}
} else if (survivor.isRunning || !currentRepairingGen) {
survivor.isRepairing = false;
}
// Check exit gate collision
if (generatorsRepaired >= 3) {
for (var i = 0; i < exitGates.length; i++) {
if (getDistance(survivor, exitGates[i]) < 50) {
survivor.escaped = true;
survivorsEscaped++;
if (survivorsEscaped >= survivors.length) {
gameState = 'won';
LK.showYouWin();
return;
}
}
}
}
statusText.setText('Generators: ' + generatorsRepaired + '/3 | Escaped: ' + survivorsEscaped + '/' + survivors.length);
// Flash effect when detected
if (killerDetected && detectionFlashTimer > 0) {
detectionFlashTimer--;
if (detectionFlashTimer % 6 < 3) {
survivor.alpha = 0.5;
} else {
survivor.alpha = 1;
}
} else {
survivor.alpha = 1;
}
} else if (gameMode === 'killer') {
// Killer mode gameplay
var moveX = touchDirectionX * killer.speed;
var moveY = touchDirectionY * killer.speed;
killer.x += moveX;
killer.y += moveY;
// Clamp killer to arena
if (killer.x < 150) killer.x = 150;
if (killer.x > 1900) killer.x = 1900;
if (killer.y < 150) killer.y = 150;
if (killer.y > 2600) killer.y = 2600;
// Collision with walls
for (var i = 0; i < walls.length; i++) {
if (getDistance(killer, walls[i]) < 50) {
killer.x -= moveX;
killer.y -= moveY;
break;
}
}
// Check if killer caught survivors
for (var s = 0; s < survivors.length; s++) {
if (!survivors[s].escaped && getDistance(killer, survivors[s]) < 50) {
gameState = 'won';
LK.showYouWin();
return;
}
}
statusText.setText('Catch the survivors! Generators: ' + generatorsRepaired + '/3');
}
// Update generator UI
for (var i = 0; i < generators.length; i++) {
generators[i].update();
}
// Game over condition
if (gameState === 'chasing') {
gameState = 'lost';
LK.showGameOver();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ExitGate = Container.expand(function () {
var self = Container.call(this);
var exitGraphics = self.attachAsset('exitGate', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Generator = Container.expand(function () {
var self = Container.call(this);
var generatorGraphics = self.attachAsset('generator', {
anchorX: 0.5,
anchorY: 0.5
});
var indicatorGraphics = self.attachAsset('generatorIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
indicatorGraphics.y = -50;
var repairText = new Text2('0%', {
size: 30,
fill: '#ffffff'
});
repairText.anchor.set(0.5, 0.5);
repairText.y = 0;
self.addChild(repairText);
self.isRepaired = false;
self.repairProgress = 0;
self.maxRepairProgress = 100;
self.update = function () {
repairText.setText(Math.floor(self.repairProgress / 100 * 100) + '%');
if (self.isRepaired) {
generatorGraphics.tint = 0x00ff00;
}
};
return self;
});
var HidingSpot = Container.expand(function () {
var self = Container.call(this);
var hidingGraphics = self.attachAsset('hidingSpot', {
anchorX: 0.5,
anchorY: 0.5
});
self.isOccupied = false;
return self;
});
var Killer = Container.expand(function () {
var self = Container.call(this);
var killerGraphics = self.attachAsset('killer', {
anchorX: 0.5,
anchorY: 0.5
});
var killerEyeLeft = self.attachAsset('killerEyes', {
anchorX: 0.5,
anchorY: 0.5
});
killerEyeLeft.x = -12;
killerEyeLeft.y = -8;
var killerEyeRight = self.attachAsset('killerEyes', {
anchorX: 0.5,
anchorY: 0.5
});
killerEyeRight.x = 12;
killerEyeRight.y = -8;
self.speed = 3.2;
self.detectionRadius = 400;
self.aggressiveRadius = 180;
self.targetSurvivor = null;
self.isChasing = false;
self.chaseTimer = 0;
self.chaseDuration = 0;
self.lastX = self.x;
self.lastY = self.y;
self.huntingMode = false;
self.predictionX = 0;
self.predictionY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var Survivor = Container.expand(function () {
var self = Container.call(this);
var survivorGraphics = self.attachAsset('survivor', {
anchorX: 0.5,
anchorY: 0.5
});
var survivorHead = self.attachAsset('survivorHead', {
anchorX: 0.5,
anchorY: 0.5
});
survivorHead.y = -15;
self.speed = 3;
self.isRunning = false;
self.isHiding = false;
self.isRepairing = false;
self.repairProgress = 0;
self.maxRepairTime = 300;
self.trailCounter = 0;
self.lastX = self.x;
self.lastY = self.y;
self.id = 0;
self.escaped = false;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var survivors = [];
var activeSurvivor = 0;
var killer;
var generators = [];
var walls = [];
var hidingSpots = [];
var exitGates = [];
var generatorsRepaired = 0;
var gameState = 'playing'; // playing, chasing, won, lost, killerMode
var dragNode = null;
var survivorTrail = [];
var killerDetected = false;
var detectionFlashTimer = 0;
var gameMode = 'survivor'; // survivor or killer
var survivorsEscaped = 0;
// Initialize UI
var statusText = new Text2('Generators: 0/3', {
size: 80,
fill: '#ffffff'
});
statusText.anchor.set(0.5, 0);
LK.gui.top.addChild(statusText);
var objectiveText = new Text2('Repair all generators to escape', {
size: 60,
fill: '#ffffff'
});
objectiveText.anchor.set(0.5, 0.5);
objectiveText.y = 80;
LK.gui.center.addChild(objectiveText);
var gameModeText = new Text2('Mode: SURVIVOR', {
size: 50,
fill: '#00ff00'
});
gameModeText.anchor.set(0.5, 0);
gameModeText.x = 1024;
gameModeText.y = 150;
game.addChild(gameModeText);
// Game mode selection UI
var modeSelectionContainer = new Container();
modeSelectionContainer.x = 0;
modeSelectionContainer.y = 0;
game.addChild(modeSelectionContainer);
var titleText = new Text2('PIXEL DREAD', {
size: 120,
fill: '#ff0000'
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
modeSelectionContainer.addChild(titleText);
var selectText = new Text2('SELECT YOUR ROLE', {
size: 80,
fill: '#ffffff'
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 900;
modeSelectionContainer.addChild(selectText);
// Survivor button
var survivorButtonBg = LK.getAsset('hidingSpot', {
anchorX: 0.5,
anchorY: 0.5
});
survivorButtonBg.x = 600;
survivorButtonBg.y = 1400;
modeSelectionContainer.addChild(survivorButtonBg);
var survivorButtonText = new Text2('SURVIVOR\n(Repair & Escape)', {
size: 50,
fill: '#00ff00'
});
survivorButtonText.anchor.set(0.5, 0.5);
survivorButtonText.x = 600;
survivorButtonText.y = 1400;
modeSelectionContainer.addChild(survivorButtonText);
// Killer button
var killerButtonBg = LK.getAsset('killer', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
killerButtonBg.x = 1450;
killerButtonBg.y = 1400;
modeSelectionContainer.addChild(killerButtonBg);
var killerButtonText = new Text2('KILLER\n(Hunt Survivors)', {
size: 50,
fill: '#ff0000'
});
killerButtonText.anchor.set(0.5, 0.5);
killerButtonText.x = 1450;
killerButtonText.y = 1400;
modeSelectionContainer.addChild(killerButtonText);
var gameStarted = false;
var modeSelectionActive = true;
// Create arena layout
function initializeArena() {
// Create perimeter walls
var wallPositions = [{
x: 100,
y: 100
}, {
x: 300,
y: 100
}, {
x: 500,
y: 100
}, {
x: 700,
y: 100
}, {
x: 900,
y: 100
}, {
x: 1100,
y: 100
}, {
x: 1300,
y: 100
}, {
x: 1500,
y: 100
}, {
x: 1700,
y: 100
}, {
x: 1900,
y: 100
}, {
x: 100,
y: 2600
}, {
x: 300,
y: 2600
}, {
x: 500,
y: 2600
}, {
x: 700,
y: 2600
}, {
x: 900,
y: 2600
}, {
x: 1100,
y: 2600
}, {
x: 1300,
y: 2600
}, {
x: 1500,
y: 2600
}, {
x: 1700,
y: 2600
}, {
x: 1900,
y: 2600
}, {
x: 100,
y: 300
}, {
x: 100,
y: 600
}, {
x: 100,
y: 900
}, {
x: 100,
y: 1200
}, {
x: 100,
y: 1500
}, {
x: 100,
y: 1800
}, {
x: 100,
y: 2100
}, {
x: 100,
y: 2400
}, {
x: 1900,
y: 300
}, {
x: 1900,
y: 600
}, {
x: 1900,
y: 900
}, {
x: 1900,
y: 1200
}, {
x: 1900,
y: 1500
}, {
x: 1900,
y: 1800
}, {
x: 1900,
y: 2100
}, {
x: 1900,
y: 2400
}];
for (var i = 0; i < wallPositions.length; i++) {
var wall = game.addChild(new Wall());
wall.x = wallPositions[i].x;
wall.y = wallPositions[i].y;
walls.push(wall);
}
// Internal obstacles
var obstaclePositions = [{
x: 400,
y: 400
}, {
x: 1600,
y: 400
}, {
x: 400,
y: 2300
}, {
x: 1600,
y: 2300
}, {
x: 1024,
y: 800
}, {
x: 1024,
y: 1900
}, {
x: 600,
y: 1350
}, {
x: 1450,
y: 1350
}];
for (var i = 0; i < obstaclePositions.length; i++) {
var wall = game.addChild(new Wall());
wall.x = obstaclePositions[i].x;
wall.y = obstaclePositions[i].y;
walls.push(wall);
}
// Create hiding spots
var hidingPositions = [{
x: 300,
y: 600
}, {
x: 1700,
y: 600
}, {
x: 300,
y: 2100
}, {
x: 1700,
y: 2100
}, {
x: 800,
y: 1350
}, {
x: 1250,
y: 1350
}];
for (var i = 0; i < hidingPositions.length; i++) {
var hiding = game.addChild(new HidingSpot());
hiding.x = hidingPositions[i].x;
hiding.y = hidingPositions[i].y;
hidingSpots.push(hiding);
}
// Create generators
var generatorPositions = [{
x: 350,
y: 1350
}, {
x: 1700,
y: 800
}, {
x: 1300,
y: 2200
}];
for (var i = 0; i < generatorPositions.length; i++) {
var gen = game.addChild(new Generator());
gen.x = generatorPositions[i].x;
gen.y = generatorPositions[i].y;
generators.push(gen);
}
// Create exit gates (spawn but locked)
var exitPositions = [{
x: 200,
y: 1350
}, {
x: 1850,
y: 1350
}];
for (var i = 0; i < exitPositions.length; i++) {
var exit = game.addChild(new ExitGate());
exit.x = exitPositions[i].x;
exit.y = exitPositions[i].y;
exitGates.push(exit);
}
}
// Create multiple survivors
var survivorSpawns = [{
x: 300,
y: 500
}, {
x: 1750,
y: 500
}, {
x: 300,
y: 2200
}, {
x: 1750,
y: 2200
}];
for (var s = 0; s < survivorSpawns.length; s++) {
var survivor = game.addChild(new Survivor());
survivor.x = survivorSpawns[s].x;
survivor.y = survivorSpawns[s].y;
survivor.id = s;
survivor.escaped = false;
survivors.push(survivor);
}
// Create killer
killer = game.addChild(new Killer());
killer.x = 1024;
killer.y = 300;
killer.mode = gameMode;
// Initialize arena
initializeArena();
// Play ambient music
LK.playMusic('ambience');
// Collision detection helper
function checkCollision(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDist = obj1.width / 2 + obj2.width / 2;
return distance < minDist;
}
// Distance calculation
function getDistance(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Obstacle collision check
function isPathBlocked(fromX, fromY, toX, toY) {
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var dx = toX - fromX;
var dy = toY - fromY;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return false;
var stepX = dx / len * 5;
var stepY = dy / len * 5;
var steps = Math.floor(len / 5);
var checkX = fromX;
var checkY = fromY;
for (var s = 0; s < steps; s++) {
checkX += stepX;
checkY += stepY;
var wallDx = checkX - wall.x;
var wallDy = checkY - wall.y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 50) return true;
}
}
return false;
}
// Movement control system
var gameWidth = 2048;
var gameHeight = 2732;
var controlZoneSize = 300;
var currentTouchId = null;
var currentTouchX = 0;
var currentTouchY = 0;
var touchDirectionX = 0;
var touchDirectionY = 0;
game.down = function (x, y, obj) {
if (modeSelectionActive) {
// Handle mode selection
if (Math.abs(x - 600) < 150 && Math.abs(y - 1400) < 150) {
gameMode = 'survivor';
killer.mode = 'survivor';
modeSelectionActive = false;
gameStarted = true;
} else if (Math.abs(x - 1450) < 150 && Math.abs(y - 1400) < 150) {
gameMode = 'killer';
killer.mode = 'killer';
modeSelectionActive = false;
gameStarted = true;
}
return;
}
if (gameState !== 'playing' && gameState !== 'killerMode') return;
currentTouchId = 1;
currentTouchX = x;
currentTouchY = y;
};
game.move = function (x, y, obj) {
if (modeSelectionActive || gameState !== 'playing' && gameState !== 'killerMode') return;
if (currentTouchId === null) return;
currentTouchX = x;
currentTouchY = y;
var centerX = gameWidth / 2;
var centerY = gameHeight / 2;
var angle = Math.atan2(currentTouchY - centerY, currentTouchX - centerX);
var distance = Math.sqrt(Math.pow(currentTouchX - centerX, 2) + Math.pow(currentTouchY - centerY, 2));
var maxDistance = 600;
var effectiveDistance = Math.min(distance, maxDistance);
touchDirectionX = Math.cos(angle) * (effectiveDistance / maxDistance);
touchDirectionY = Math.sin(angle) * (effectiveDistance / maxDistance);
};
game.up = function (x, y, obj) {
if (modeSelectionActive) return;
currentTouchId = null;
touchDirectionX = 0;
touchDirectionY = 0;
};
// Main game update loop
game.update = function () {
// Handle mode selection screen
if (modeSelectionActive) {
statusText.visible = false;
objectiveText.visible = false;
gameModeText.visible = false;
return;
}
statusText.visible = true;
objectiveText.visible = true;
gameModeText.visible = true;
if (gameState !== 'playing' && gameState !== 'killerMode') return;
// Update all survivors
for (var s = 0; s < survivors.length; s++) {
survivors[s].update();
}
killer.update();
if (gameMode === 'survivor') {
// Survivor mode gameplay
var survivor = survivors[activeSurvivor];
// Apply directional touch-based movement
var moveX = touchDirectionX * survivor.speed;
var moveY = touchDirectionY * survivor.speed;
survivor.x += moveX;
survivor.y += moveY;
survivor.isRunning = Math.abs(touchDirectionX) > 0.1 || Math.abs(touchDirectionY) > 0.1;
var canMove = true;
for (var i = 0; i < walls.length; i++) {
if (getDistance({
x: survivor.x,
y: survivor.y
}, walls[i]) < 50) {
canMove = false;
survivor.x -= moveX;
survivor.y -= moveY;
break;
}
}
// Clamp survivor to arena
if (survivor.x < 150) survivor.x = 150;
if (survivor.x > 1900) survivor.x = 1900;
if (survivor.y < 150) survivor.y = 150;
if (survivor.y > 2600) survivor.y = 2600;
// Update killer AI with enhanced difficulty
var closestDist = 999999;
var closestSurvivor = null;
for (var s = 0; s < survivors.length; s++) {
if (!survivors[s].escaped) {
var dist = getDistance(killer, survivors[s]);
if (dist < closestDist) {
closestDist = dist;
closestSurvivor = survivors[s];
}
}
}
if (closestSurvivor) {
if (closestDist < killer.detectionRadius) {
killerDetected = true;
detectionFlashTimer = 30;
killer.targetSurvivor = closestSurvivor;
// Predictive movement for harder AI
var predictX = closestSurvivor.x;
var predictY = closestSurvivor.y;
if (closestSurvivor.isRunning) {
var distX = closestSurvivor.x - closestSurvivor.lastX;
var distY = closestSurvivor.y - closestSurvivor.lastY;
predictX += distX * 8;
predictY += distY * 8;
}
killer.predictionX = predictX;
killer.predictionY = predictY;
// Killer moves toward prediction point
if (closestDist > 30) {
var angle = Math.atan2(predictY - killer.y, predictX - killer.x);
var killerSpeed = closestDist < killer.aggressiveRadius ? killer.speed * 2 : killer.speed;
var killerNewX = killer.x + Math.cos(angle) * killerSpeed;
var killerNewY = killer.y + Math.sin(angle) * killerSpeed;
var canKillerMove = true;
for (var i = 0; i < walls.length; i++) {
if (getDistance({
x: killerNewX,
y: killerNewY
}, walls[i]) < 50) {
canKillerMove = false;
break;
}
}
if (canKillerMove) {
killer.x = killerNewX;
killer.y = killerNewY;
}
}
if (closestDist < 50) {
gameState = 'chasing';
LK.getSound('chase').play();
}
} else {
killerDetected = false;
}
}
// Update hiding
survivor.isHiding = false;
for (var i = 0; i < hidingSpots.length; i++) {
if (getDistance(survivor, hidingSpots[i]) < 45) {
survivor.isHiding = true;
break;
}
}
// Check generator repair
var currentRepairingGen = null;
for (var i = 0; i < generators.length; i++) {
var gen = generators[i];
if (!gen.isRepaired && getDistance(survivor, gen) < 60 && !survivor.isHiding && !survivor.isRunning) {
currentRepairingGen = gen;
break;
}
}
if (currentRepairingGen && !survivor.isRunning) {
survivor.isRepairing = true;
survivor.repairProgress += 1;
currentRepairingGen.repairProgress = survivor.repairProgress;
if (survivor.repairProgress >= 300) {
currentRepairingGen.isRepaired = true;
generatorsRepaired++;
survivor.isRepairing = false;
survivor.repairProgress = 0;
LK.getSound('repair').play();
}
} else if (survivor.isRunning || !currentRepairingGen) {
survivor.isRepairing = false;
}
// Check exit gate collision
if (generatorsRepaired >= 3) {
for (var i = 0; i < exitGates.length; i++) {
if (getDistance(survivor, exitGates[i]) < 50) {
survivor.escaped = true;
survivorsEscaped++;
if (survivorsEscaped >= survivors.length) {
gameState = 'won';
LK.showYouWin();
return;
}
}
}
}
statusText.setText('Generators: ' + generatorsRepaired + '/3 | Escaped: ' + survivorsEscaped + '/' + survivors.length);
// Flash effect when detected
if (killerDetected && detectionFlashTimer > 0) {
detectionFlashTimer--;
if (detectionFlashTimer % 6 < 3) {
survivor.alpha = 0.5;
} else {
survivor.alpha = 1;
}
} else {
survivor.alpha = 1;
}
} else if (gameMode === 'killer') {
// Killer mode gameplay
var moveX = touchDirectionX * killer.speed;
var moveY = touchDirectionY * killer.speed;
killer.x += moveX;
killer.y += moveY;
// Clamp killer to arena
if (killer.x < 150) killer.x = 150;
if (killer.x > 1900) killer.x = 1900;
if (killer.y < 150) killer.y = 150;
if (killer.y > 2600) killer.y = 2600;
// Collision with walls
for (var i = 0; i < walls.length; i++) {
if (getDistance(killer, walls[i]) < 50) {
killer.x -= moveX;
killer.y -= moveY;
break;
}
}
// Check if killer caught survivors
for (var s = 0; s < survivors.length; s++) {
if (!survivors[s].escaped && getDistance(killer, survivors[s]) < 50) {
gameState = 'won';
LK.showYouWin();
return;
}
}
statusText.setText('Catch the survivors! Generators: ' + generatorsRepaired + '/3');
}
// Update generator UI
for (var i = 0; i < generators.length; i++) {
generators[i].update();
}
// Game over condition
if (gameState === 'chasing') {
gameState = 'lost';
LK.showGameOver();
}
};