User prompt
make background much more darker
User prompt
Prevent enemies from spawning too close to the player character. Define a safe spawn-free zone around the player with a radius of at least 200 pixels. Enemies should only spawn outside this radius. This ensures that enemies do not appear directly on top of or too close to the player during gameplay.
User prompt
Soften the flashlight beam by lowering its opacity and adjusting its visual style. The light should appear semi-transparent (around 50–70% opacity) with smooth, feathered edges. Avoid hard lines or overly bright visuals. The beam should gently blend into the darkness, giving a subtle but effective illumination that preserves the game's horror atmospher
User prompt
Add a directional flashlight beam that follows the player's facing direction. The beam should be a cone of light extending from the front of the character. If the player faces right, the beam points right; if the player turns left, the beam flips and points left. The light should be attached to the character and update direction instantly based on movement.
User prompt
Make the player character flip direction based on movement input. If the player moves left, the character should face left (flip the sprite horizontally). If the player moves right, the character should face right (default sprite). This should happen instantly and smoothly as the player changes direction during gameplay.
User prompt
yaratık hızını çok yavaşlat
User prompt
yaratıklar çok hızlı, yavaşlat
User prompt
Remove the flashlight light beam from the game entirely. The player should no longer emit any light or have any active lighting effect. Clear all visual, functional, and collision elements related to the flashlight. This includes light animations, enemy interactions, and UI indicators tied to the flashlight system.
User prompt
Ensure that when the flashlight beam touches or overlaps with an enemy, it does not cause a game over or trigger any failure state. The player should only lose the game if the enemy physically touches the player’s character sprite. Flashlight contact should not be treated as collision or threat for the player.
User prompt
When the flashlight beam hits an enemy, it does not cause damage or defeat them, but temporarily stuns them for 1–2 seconds. During this time, the enemy should stop moving and slightly flicker or change color to indicate the effect. After the stun duration, they resume their normal behavior. This allows players to escape but not abuse the flashlight for permanent safety.
User prompt
Reduce the opacity of the flashlight beam held by the player to make it more transparent and atmospheric, without losing its directional effect. The beam should softly illuminate the area but not appear too bright or solid. Update collision rules so that the player only loses the game if a creature directly touches their character sprite. Flashlight beam contact should not trigger a game over. Enemies can move through the beam without dying or reacting unless otherwise scripted.
User prompt
yaratık sayılarını biraz azalt
User prompt
"Talismans Left: 2 | Letters Left: 3" yazılarını ekranın üst bölümüne ortala
User prompt
Increase the number of letters and talismans the player must collect in each level. Spread them more widely across the map, encouraging exploration. Display an on-screen counter showing how many items remain, such as: "Talismans Left: 2 | Letters Left: 3" The counter should update in real time as the player collects each item. If the player attempts to enter Galata Tower without collecting all required letters and talismans, block entry and show a message: "You must collect all letters and talismans before entering." The UI for the counter should be small, readable, and positioned in a corner without distracting from gameplay.
User prompt
At the start of each level, ensure no enemy or creature spawns within a close radius around the player. Define a safe zone around the player character (e.g., 200 pixels in all directions)
User prompt
Galata towers opacity make %100
User prompt
Move the Galata Tower sprite down by 70 pixels on the Y-axis.
User prompt
Move the Galata Tower sprite down by 40 pixels on the Y-axis.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Fog overlay var Fog = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('fog', { anchorX: 0, anchorY: 0, alpha: 0.18 }); self.update = function () { // Animate fog alpha for effect self.asset.alpha = DIFFICULTY.fogAlpha + Math.sin(LK.ticks / 60) * 0.04; }; return self; }); // Galata Tower (goal) var Galata = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('galata', { anchorX: 0.5, anchorY: 1 }); return self; }); // Ghost base class var Ghost = Container.expand(function () { var self = Container.call(this); self.type = 'cin'; self.speed = 2; self.target = null; self.state = 'idle'; // idle, chasing, jumpscare self.lastIntersecting = false; self.jumpscarePlayed = false; self.asset = null; self.init = function (type) { self.type = type; var assetId = 'ghost_' + type; self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); }; self.update = function () { if (!self.target) return; // Only chase if in flashlight cone var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 800) { self.state = 'chasing'; var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } else { self.state = 'idle'; } }; return self; }); // Letter collectible var Letter = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('letter', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; return self; }); // Survivor (player) class var Survivor = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('survivor', { anchorX: 0.5, anchorY: 0.5 }); // Flashlight beam var flashlight = self.attachAsset('flashlight', { anchorX: 0.1, anchorY: 0.5, x: 60, y: 0, alpha: 0.45 }); flashlight.zIndex = 1; self.flashlight = flashlight; self.facing = 1; // 1: right, -1: left // Flicker flashlight self.flickerTimer = 0; self.update = function () { self.flickerTimer++; if (self.flickerTimer % DIFFICULTY.flashlightFlicker === 0) { // Reduce visibility and increase flicker flashlight.alpha = 0.28 + Math.random() * (0.18 + 0.04 * (DIFFICULTY.run - 1)); flashlight.scaleX = 0.85 + Math.random() * (0.12 - 0.01 * (DIFFICULTY.run - 1)); flashlight.scaleY = 0.85 + Math.random() * (0.12 - 0.01 * (DIFFICULTY.run - 1)); } // Keep flashlight facing flashlight.scaleX = Math.abs(flashlight.scaleX) * self.facing; }; return self; }); // Talisman collectible var Talisman = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('talisman', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x07070a }); /**** * Game Code ****/ // Music // Sound effects // Fog overlay (large, semi-transparent) // Galata Tower (goal) // Letter (collectible) // Talisman (collectible) // Ghosts (cin, karabasan, gulyabani) - different colors // Flashlight beam (ellipse, semi-transparent yellow) // Main character: "Survivor" // Play music // Persistent run counter for progressive difficulty var nightfall_runCount = parseInt(storage.nightfall_runCount || "0", 10); nightfall_runCount++; storage.nightfall_runCount = nightfall_runCount; // Difficulty scaling factors var DIFFICULTY = { run: nightfall_runCount, ghostSpeedMult: 1 + 0.15 * (nightfall_runCount - 1), ghostCountAdd: Math.min(nightfall_runCount - 1, 3), talismanCount: Math.max(1, 3 - Math.floor((nightfall_runCount - 1) / 2)), letterCount: Math.max(1, 2 - Math.floor((nightfall_runCount - 1) / 3)), fogAlpha: 0.18 + 0.04 * Math.min(nightfall_runCount - 1, 4), flashlightFlicker: Math.max(6, 8 - (nightfall_runCount - 1)), jumpscareVolume: Math.min(1, 0.7 + 0.1 * (nightfall_runCount - 1)), message: nightfall_runCount > 1 ? "They are watching closer this time..." : "" }; LK.playMusic('nightfall_bgm', { loop: true, fade: { start: 0, end: 0.22, duration: 2000 } }); // Survivor/player var survivor = new Survivor(); game.addChild(survivor); survivor.x = 400; survivor.y = 2000; // Galata Tower (goal) var galata = new Galata(); game.addChild(galata); galata.x = 2048 / 2; galata.y = 760; // moved down by 40 more pixels // Fog overlay var fog = new Fog(); game.addChild(fog); // Score: talismans collected var talismanScore = 0; var talismanTxt = new Text2('Talismans: 0', { size: 80, fill: 0xFFE066 }); talismanTxt.anchor.set(0.5, 0); LK.gui.top.addChild(talismanTxt); // Letters collected var letterScore = 0; var letterTxt = new Text2('Letters: 0', { size: 60, fill: 0xFFFFFF }); letterTxt.anchor.set(0.5, 0); LK.gui.top.addChild(letterTxt); letterTxt.y = 90; // Timer (sunrise in 60 seconds) var timeLeft = 60; var timerTxt = new Text2('01:00', { size: 80, fill: 0xFFB347 }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); timerTxt.y = 180; // Subtle difficulty message var diffMsg = null; if (DIFFICULTY.message) { diffMsg = new Text2(DIFFICULTY.message, { size: 48, fill: 0xFF6666, font: "monospace", alpha: 0.7 }); diffMsg.anchor.set(0.5, 0); diffMsg.x = LK.gui.top.width / 2; diffMsg.y = 260; LK.gui.top.addChild(diffMsg); // Fade out after a few seconds tween(diffMsg, { alpha: 0 }, { duration: 4000, onFinish: function onFinish() { diffMsg.destroy(); } }); } // Place collectibles var talismans = []; var letters = []; for (var i = 0; i < DIFFICULTY.talismanCount; i++) { var t = new Talisman(); t.x = 400 + Math.random() * 1200; t.y = 600 + Math.random() * 1600; talismans.push(t); game.addChild(t); } for (var i = 0; i < DIFFICULTY.letterCount; i++) { var l = new Letter(); l.x = 300 + Math.random() * 1400; l.y = 700 + Math.random() * 1500; letters.push(l); game.addChild(l); } // Place ghosts var ghosts = []; function spawnGhost(type, x, y, speed) { var g = new Ghost(); g.init(type); g.x = x; g.y = y; g.speed = speed; g.target = survivor; ghosts.push(g); game.addChild(g); } // Base ghosts spawnGhost('cin', 1800, 2200, 2.7 * DIFFICULTY.ghostSpeedMult); spawnGhost('karabasan', 200, 800, 2.2 * DIFFICULTY.ghostSpeedMult); spawnGhost('gulyabani', 1800, 600, 1.8 * DIFFICULTY.ghostSpeedMult); // Add more ghosts for extra challenge spawnGhost('cin', 400, 1200, 2.5 * DIFFICULTY.ghostSpeedMult); spawnGhost('karabasan', 1600, 1800, 2.0 * DIFFICULTY.ghostSpeedMult); spawnGhost('gulyabani', 600, 400, 1.7 * DIFFICULTY.ghostSpeedMult); // Progressive extra ghosts for (var i = 0; i < DIFFICULTY.ghostCountAdd; i++) { var types = ['cin', 'karabasan', 'gulyabani']; var type = types[i % types.length]; var x = 200 + Math.random() * 1600; var y = 600 + Math.random() * 1800; var baseSpeed = type === 'cin' ? 2.7 : type === 'karabasan' ? 2.2 : 1.8; spawnGhost(type, x, y, baseSpeed * (1.1 + 0.08 * i) * DIFFICULTY.ghostSpeedMult); } // Dragging logic var dragNode = null; function handleMove(x, y, obj) { if (dragNode) { // Clamp to game area var nx = Math.max(60, Math.min(2048 - 60, x)); var ny = Math.max(200, Math.min(2732 - 60, y)); dragNode.x = nx; dragNode.y = ny; // Face direction if (nx > survivor.x) survivor.facing = 1;else if (nx < survivor.x) survivor.facing = -1; } } game.move = handleMove; game.down = function (x, y, obj) { // Only allow dragging survivor if (x > 100 || y > 100) { // avoid top left menu dragNode = survivor; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Jumpscare state var jumpscareActive = false; var jumpscareTimer = 0; // Game update game.update = function () { // Update survivor survivor.update(); // Update fog fog.update(); // Update ghosts for (var i = 0; i < ghosts.length; i++) { var g = ghosts[i]; g.update(); // Check if ghost is in flashlight cone var dx = g.x - survivor.x; var dy = g.y - survivor.y; var angle = Math.atan2(dy, dx); var facing = survivor.facing; var inCone = false; if (facing === 1 && dx > 0 && Math.abs(angle) < Math.PI / 4) inCone = true; if (facing === -1 && dx < 0 && Math.abs(angle - Math.PI) < Math.PI / 4) inCone = true; // If in cone, slow ghost if (inCone) g.speed = 1.1;else g.speed = g.type === 'cin' ? 2.2 : g.type === 'karabasan' ? 1.7 : 1.3; // Jumpscare: if ghost intersects survivor var intersecting = g.intersects(survivor); if (!g.lastIntersecting && intersecting && !jumpscareActive) { jumpscareActive = true; jumpscareTimer = 0; LK.effects.flashScreen(0xffffff, 400 + 100 * (DIFFICULTY.run - 1)); var jumpscareSound = LK.getSound('jumpscare'); if (jumpscareSound) jumpscareSound.setVolume ? jumpscareSound.setVolume(DIFFICULTY.jumpscareVolume) : null; LK.getSound('jumpscare').play(); tween(g.asset, { alpha: 1 }, { duration: 200, onFinish: function onFinish() {} }); // Extra visual effect for high difficulty if (DIFFICULTY.run > 2) { LK.effects.flashScreen(0xff0000, 200); } } g.lastIntersecting = intersecting; } // Jumpscare effect if (jumpscareActive) { jumpscareTimer++; // On first frame of jumpscare, show full-screen jumpscare image and start effects if (jumpscareTimer === 1) { // Freeze player input dragNode = null; // Add full-screen jumpscare image (random ghost close-up) var jumpscareGhosts = ['ghost_karabasan', 'ghost_cin', 'ghost_gulyabani']; var jumpscareId = jumpscareGhosts[Math.floor(Math.random() * jumpscareGhosts.length)]; var jumpscareImg = LK.getAsset(jumpscareId, { anchorX: 0.5, anchorY: 0.5, scaleX: 4.5, scaleY: 4.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.98 }); jumpscareImg.zIndex = 9999; game.addChild(jumpscareImg); // Add glowing eyes effect (simple white ellipse overlay) var eyes = []; for (var e = 0; e < 2; e++) { var eye = LK.getAsset('flashlight', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.18, scaleY: 0.09, x: 2048 / 2 + (e === 0 ? -120 : 120), y: 2732 / 2 - 60, alpha: 0.85, tint: 0xffffff }); eye.zIndex = 10000; game.addChild(eye); eyes.push(eye); } // Screen shake/glitch effect var shakeTimer = 0; var shakeInterval = LK.setInterval(function () { shakeTimer++; var shakeAmount = 32 - Math.floor(shakeTimer / 2); if (shakeAmount < 2) shakeAmount = 2; game.x = (Math.random() - 0.5) * shakeAmount; game.y = (Math.random() - 0.5) * shakeAmount; // Glitch: randomize alpha and scale jumpscareImg.alpha = 0.93 + Math.random() * 0.07; jumpscareImg.scaleX = 4.3 + Math.random() * 0.4; jumpscareImg.scaleY = 4.3 + Math.random() * 0.4; for (var e = 0; e < eyes.length; e++) { eyes[e].alpha = 0.7 + Math.random() * 0.3; } if (shakeTimer > 30) { // ~0.5s LK.clearInterval(shakeInterval); game.x = 0; game.y = 0; // Fade out jumpscare image and eyes tween(jumpscareImg, { alpha: 0 }, { duration: 400, onFinish: function onFinish() { jumpscareImg.destroy(); for (var e = 0; e < eyes.length; e++) eyes[e].destroy(); } }); } }, 16); // After jumpscare, show dark "You Died" overlay and options LK.setTimeout(function () { // Overlay var overlay = new Container(); var bg = LK.getAsset('fog', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1, scaleY: 1, alpha: 0.92, tint: 0x000000 }); overlay.addChild(bg); // "You Died" text var diedTxt = new Text2('YOU DIED', { size: 220, fill: 0xFF2222, font: "monospace", alpha: 0.93 }); diedTxt.anchor.set(0.5, 0.5); diedTxt.x = 2048 / 2; diedTxt.y = 1100; overlay.addChild(diedTxt); // Subtle ambient message var msgTxt = new Text2('The night is endless...', { size: 70, fill: 0xcccccc, font: "monospace", alpha: 0.7 }); msgTxt.anchor.set(0.5, 0.5); msgTxt.x = 2048 / 2; msgTxt.y = 1400; overlay.addChild(msgTxt); // Retry button var retryBtn = new Text2('Retry from last chapter', { size: 90, fill: 0xFFB347, font: "monospace", alpha: 0.95 }); retryBtn.anchor.set(0.5, 0.5); retryBtn.x = 2048 / 2; retryBtn.y = 1700; overlay.addChild(retryBtn); // Main menu button var menuBtn = new Text2('Return to main menu', { size: 80, fill: 0xcccccc, font: "monospace", alpha: 0.85 }); menuBtn.anchor.set(0.5, 0.5); menuBtn.x = 2048 / 2; menuBtn.y = 1850; overlay.addChild(menuBtn); // Add overlay to game overlay.zIndex = 10001; game.addChild(overlay); // Faint ambient noise (reuse jumpscare sound at low volume) var jumpscareSound = LK.getSound('jumpscare'); if (jumpscareSound && jumpscareSound.setVolume) { jumpscareSound.setVolume(0.12); jumpscareSound.play(); } // Button logic retryBtn.interactive = true; retryBtn.down = function () { // Remove overlay and restart game overlay.destroy(); LK.showGameOver(); // Triggers game reset }; menuBtn.interactive = true; menuBtn.down = function () { overlay.destroy(); LK.showGameOver(); // Let LK engine handle main menu }; }, 900); } return; } // Collect talismans for (var i = talismans.length - 1; i >= 0; i--) { var t = talismans[i]; if (!t.collected && survivor.intersects(t)) { t.collected = true; LK.getSound('talisman').play(); talismanScore++; talismanTxt.setText('Talismans: ' + talismanScore); tween(t, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { t.destroy(); } }); talismans.splice(i, 1); } } // Collect letters for (var i = letters.length - 1; i >= 0; i--) { var l = letters[i]; if (!l.collected && survivor.intersects(l)) { l.collected = true; LK.getSound('collect').play(); letterScore++; letterTxt.setText('Letters: ' + letterScore); tween(l, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { l.destroy(); } }); letters.splice(i, 1); } } // Win condition: reach Galata Tower if (survivor.intersects(galata)) { LK.effects.flashScreen(0x00ffcc, 800); LK.showYouWin(); } // --- Play subtle background sound effect at random intervals (5-10s) --- var backgroundSoundTimer = null; function playBackgroundSoundLoop() { // Play the background sound effect at low volume var bgSound = LK.getSound('background'); if (bgSound && bgSound.setVolume) { bgSound.setVolume(0.18); // subtle, blends with ambient } if (bgSound) { bgSound.play(); } // Schedule next play at a random interval between 5-10 seconds var nextDelay = 5000 + Math.floor(Math.random() * 5000); // ms backgroundSoundTimer = LK.setTimeout(playBackgroundSoundLoop, nextDelay); } // Start the background sound loop playBackgroundSoundLoop(); // Timer if (LK.ticks % 60 === 0 && timeLeft > 0 && !jumpscareActive) { timeLeft--; var min = Math.floor(timeLeft / 60); var sec = timeLeft % 60; timerTxt.setText((min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec); if (timeLeft === 0) { // Time up: game over LK.effects.flashScreen(0x000000, 1200); LK.setTimeout(function () { LK.showGameOver(); }, 1200); } } }; // Center GUI elements talismanTxt.x = LK.gui.top.width / 2; letterTxt.x = LK.gui.top.width / 2; timerTxt.x = LK.gui.top.width / 2;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Fog overlay
var Fog = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('fog', {
anchorX: 0,
anchorY: 0,
alpha: 0.18
});
self.update = function () {
// Animate fog alpha for effect
self.asset.alpha = DIFFICULTY.fogAlpha + Math.sin(LK.ticks / 60) * 0.04;
};
return self;
});
// Galata Tower (goal)
var Galata = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('galata', {
anchorX: 0.5,
anchorY: 1
});
return self;
});
// Ghost base class
var Ghost = Container.expand(function () {
var self = Container.call(this);
self.type = 'cin';
self.speed = 2;
self.target = null;
self.state = 'idle'; // idle, chasing, jumpscare
self.lastIntersecting = false;
self.jumpscarePlayed = false;
self.asset = null;
self.init = function (type) {
self.type = type;
var assetId = 'ghost_' + type;
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
};
self.update = function () {
if (!self.target) return;
// Only chase if in flashlight cone
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 800) {
self.state = 'chasing';
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else {
self.state = 'idle';
}
};
return self;
});
// Letter collectible
var Letter = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('letter', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
return self;
});
// Survivor (player) class
var Survivor = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('survivor', {
anchorX: 0.5,
anchorY: 0.5
});
// Flashlight beam
var flashlight = self.attachAsset('flashlight', {
anchorX: 0.1,
anchorY: 0.5,
x: 60,
y: 0,
alpha: 0.45
});
flashlight.zIndex = 1;
self.flashlight = flashlight;
self.facing = 1; // 1: right, -1: left
// Flicker flashlight
self.flickerTimer = 0;
self.update = function () {
self.flickerTimer++;
if (self.flickerTimer % DIFFICULTY.flashlightFlicker === 0) {
// Reduce visibility and increase flicker
flashlight.alpha = 0.28 + Math.random() * (0.18 + 0.04 * (DIFFICULTY.run - 1));
flashlight.scaleX = 0.85 + Math.random() * (0.12 - 0.01 * (DIFFICULTY.run - 1));
flashlight.scaleY = 0.85 + Math.random() * (0.12 - 0.01 * (DIFFICULTY.run - 1));
}
// Keep flashlight facing
flashlight.scaleX = Math.abs(flashlight.scaleX) * self.facing;
};
return self;
});
// Talisman collectible
var Talisman = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('talisman', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x07070a
});
/****
* Game Code
****/
// Music
// Sound effects
// Fog overlay (large, semi-transparent)
// Galata Tower (goal)
// Letter (collectible)
// Talisman (collectible)
// Ghosts (cin, karabasan, gulyabani) - different colors
// Flashlight beam (ellipse, semi-transparent yellow)
// Main character: "Survivor"
// Play music
// Persistent run counter for progressive difficulty
var nightfall_runCount = parseInt(storage.nightfall_runCount || "0", 10);
nightfall_runCount++;
storage.nightfall_runCount = nightfall_runCount;
// Difficulty scaling factors
var DIFFICULTY = {
run: nightfall_runCount,
ghostSpeedMult: 1 + 0.15 * (nightfall_runCount - 1),
ghostCountAdd: Math.min(nightfall_runCount - 1, 3),
talismanCount: Math.max(1, 3 - Math.floor((nightfall_runCount - 1) / 2)),
letterCount: Math.max(1, 2 - Math.floor((nightfall_runCount - 1) / 3)),
fogAlpha: 0.18 + 0.04 * Math.min(nightfall_runCount - 1, 4),
flashlightFlicker: Math.max(6, 8 - (nightfall_runCount - 1)),
jumpscareVolume: Math.min(1, 0.7 + 0.1 * (nightfall_runCount - 1)),
message: nightfall_runCount > 1 ? "They are watching closer this time..." : ""
};
LK.playMusic('nightfall_bgm', {
loop: true,
fade: {
start: 0,
end: 0.22,
duration: 2000
}
});
// Survivor/player
var survivor = new Survivor();
game.addChild(survivor);
survivor.x = 400;
survivor.y = 2000;
// Galata Tower (goal)
var galata = new Galata();
game.addChild(galata);
galata.x = 2048 / 2;
galata.y = 760; // moved down by 40 more pixels
// Fog overlay
var fog = new Fog();
game.addChild(fog);
// Score: talismans collected
var talismanScore = 0;
var talismanTxt = new Text2('Talismans: 0', {
size: 80,
fill: 0xFFE066
});
talismanTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(talismanTxt);
// Letters collected
var letterScore = 0;
var letterTxt = new Text2('Letters: 0', {
size: 60,
fill: 0xFFFFFF
});
letterTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(letterTxt);
letterTxt.y = 90;
// Timer (sunrise in 60 seconds)
var timeLeft = 60;
var timerTxt = new Text2('01:00', {
size: 80,
fill: 0xFFB347
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 180;
// Subtle difficulty message
var diffMsg = null;
if (DIFFICULTY.message) {
diffMsg = new Text2(DIFFICULTY.message, {
size: 48,
fill: 0xFF6666,
font: "monospace",
alpha: 0.7
});
diffMsg.anchor.set(0.5, 0);
diffMsg.x = LK.gui.top.width / 2;
diffMsg.y = 260;
LK.gui.top.addChild(diffMsg);
// Fade out after a few seconds
tween(diffMsg, {
alpha: 0
}, {
duration: 4000,
onFinish: function onFinish() {
diffMsg.destroy();
}
});
}
// Place collectibles
var talismans = [];
var letters = [];
for (var i = 0; i < DIFFICULTY.talismanCount; i++) {
var t = new Talisman();
t.x = 400 + Math.random() * 1200;
t.y = 600 + Math.random() * 1600;
talismans.push(t);
game.addChild(t);
}
for (var i = 0; i < DIFFICULTY.letterCount; i++) {
var l = new Letter();
l.x = 300 + Math.random() * 1400;
l.y = 700 + Math.random() * 1500;
letters.push(l);
game.addChild(l);
}
// Place ghosts
var ghosts = [];
function spawnGhost(type, x, y, speed) {
var g = new Ghost();
g.init(type);
g.x = x;
g.y = y;
g.speed = speed;
g.target = survivor;
ghosts.push(g);
game.addChild(g);
}
// Base ghosts
spawnGhost('cin', 1800, 2200, 2.7 * DIFFICULTY.ghostSpeedMult);
spawnGhost('karabasan', 200, 800, 2.2 * DIFFICULTY.ghostSpeedMult);
spawnGhost('gulyabani', 1800, 600, 1.8 * DIFFICULTY.ghostSpeedMult);
// Add more ghosts for extra challenge
spawnGhost('cin', 400, 1200, 2.5 * DIFFICULTY.ghostSpeedMult);
spawnGhost('karabasan', 1600, 1800, 2.0 * DIFFICULTY.ghostSpeedMult);
spawnGhost('gulyabani', 600, 400, 1.7 * DIFFICULTY.ghostSpeedMult);
// Progressive extra ghosts
for (var i = 0; i < DIFFICULTY.ghostCountAdd; i++) {
var types = ['cin', 'karabasan', 'gulyabani'];
var type = types[i % types.length];
var x = 200 + Math.random() * 1600;
var y = 600 + Math.random() * 1800;
var baseSpeed = type === 'cin' ? 2.7 : type === 'karabasan' ? 2.2 : 1.8;
spawnGhost(type, x, y, baseSpeed * (1.1 + 0.08 * i) * DIFFICULTY.ghostSpeedMult);
}
// Dragging logic
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area
var nx = Math.max(60, Math.min(2048 - 60, x));
var ny = Math.max(200, Math.min(2732 - 60, y));
dragNode.x = nx;
dragNode.y = ny;
// Face direction
if (nx > survivor.x) survivor.facing = 1;else if (nx < survivor.x) survivor.facing = -1;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow dragging survivor
if (x > 100 || y > 100) {
// avoid top left menu
dragNode = survivor;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Jumpscare state
var jumpscareActive = false;
var jumpscareTimer = 0;
// Game update
game.update = function () {
// Update survivor
survivor.update();
// Update fog
fog.update();
// Update ghosts
for (var i = 0; i < ghosts.length; i++) {
var g = ghosts[i];
g.update();
// Check if ghost is in flashlight cone
var dx = g.x - survivor.x;
var dy = g.y - survivor.y;
var angle = Math.atan2(dy, dx);
var facing = survivor.facing;
var inCone = false;
if (facing === 1 && dx > 0 && Math.abs(angle) < Math.PI / 4) inCone = true;
if (facing === -1 && dx < 0 && Math.abs(angle - Math.PI) < Math.PI / 4) inCone = true;
// If in cone, slow ghost
if (inCone) g.speed = 1.1;else g.speed = g.type === 'cin' ? 2.2 : g.type === 'karabasan' ? 1.7 : 1.3;
// Jumpscare: if ghost intersects survivor
var intersecting = g.intersects(survivor);
if (!g.lastIntersecting && intersecting && !jumpscareActive) {
jumpscareActive = true;
jumpscareTimer = 0;
LK.effects.flashScreen(0xffffff, 400 + 100 * (DIFFICULTY.run - 1));
var jumpscareSound = LK.getSound('jumpscare');
if (jumpscareSound) jumpscareSound.setVolume ? jumpscareSound.setVolume(DIFFICULTY.jumpscareVolume) : null;
LK.getSound('jumpscare').play();
tween(g.asset, {
alpha: 1
}, {
duration: 200,
onFinish: function onFinish() {}
});
// Extra visual effect for high difficulty
if (DIFFICULTY.run > 2) {
LK.effects.flashScreen(0xff0000, 200);
}
}
g.lastIntersecting = intersecting;
}
// Jumpscare effect
if (jumpscareActive) {
jumpscareTimer++;
// On first frame of jumpscare, show full-screen jumpscare image and start effects
if (jumpscareTimer === 1) {
// Freeze player input
dragNode = null;
// Add full-screen jumpscare image (random ghost close-up)
var jumpscareGhosts = ['ghost_karabasan', 'ghost_cin', 'ghost_gulyabani'];
var jumpscareId = jumpscareGhosts[Math.floor(Math.random() * jumpscareGhosts.length)];
var jumpscareImg = LK.getAsset(jumpscareId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.5,
scaleY: 4.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0.98
});
jumpscareImg.zIndex = 9999;
game.addChild(jumpscareImg);
// Add glowing eyes effect (simple white ellipse overlay)
var eyes = [];
for (var e = 0; e < 2; e++) {
var eye = LK.getAsset('flashlight', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.18,
scaleY: 0.09,
x: 2048 / 2 + (e === 0 ? -120 : 120),
y: 2732 / 2 - 60,
alpha: 0.85,
tint: 0xffffff
});
eye.zIndex = 10000;
game.addChild(eye);
eyes.push(eye);
}
// Screen shake/glitch effect
var shakeTimer = 0;
var shakeInterval = LK.setInterval(function () {
shakeTimer++;
var shakeAmount = 32 - Math.floor(shakeTimer / 2);
if (shakeAmount < 2) shakeAmount = 2;
game.x = (Math.random() - 0.5) * shakeAmount;
game.y = (Math.random() - 0.5) * shakeAmount;
// Glitch: randomize alpha and scale
jumpscareImg.alpha = 0.93 + Math.random() * 0.07;
jumpscareImg.scaleX = 4.3 + Math.random() * 0.4;
jumpscareImg.scaleY = 4.3 + Math.random() * 0.4;
for (var e = 0; e < eyes.length; e++) {
eyes[e].alpha = 0.7 + Math.random() * 0.3;
}
if (shakeTimer > 30) {
// ~0.5s
LK.clearInterval(shakeInterval);
game.x = 0;
game.y = 0;
// Fade out jumpscare image and eyes
tween(jumpscareImg, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
jumpscareImg.destroy();
for (var e = 0; e < eyes.length; e++) eyes[e].destroy();
}
});
}
}, 16);
// After jumpscare, show dark "You Died" overlay and options
LK.setTimeout(function () {
// Overlay
var overlay = new Container();
var bg = LK.getAsset('fog', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
alpha: 0.92,
tint: 0x000000
});
overlay.addChild(bg);
// "You Died" text
var diedTxt = new Text2('YOU DIED', {
size: 220,
fill: 0xFF2222,
font: "monospace",
alpha: 0.93
});
diedTxt.anchor.set(0.5, 0.5);
diedTxt.x = 2048 / 2;
diedTxt.y = 1100;
overlay.addChild(diedTxt);
// Subtle ambient message
var msgTxt = new Text2('The night is endless...', {
size: 70,
fill: 0xcccccc,
font: "monospace",
alpha: 0.7
});
msgTxt.anchor.set(0.5, 0.5);
msgTxt.x = 2048 / 2;
msgTxt.y = 1400;
overlay.addChild(msgTxt);
// Retry button
var retryBtn = new Text2('Retry from last chapter', {
size: 90,
fill: 0xFFB347,
font: "monospace",
alpha: 0.95
});
retryBtn.anchor.set(0.5, 0.5);
retryBtn.x = 2048 / 2;
retryBtn.y = 1700;
overlay.addChild(retryBtn);
// Main menu button
var menuBtn = new Text2('Return to main menu', {
size: 80,
fill: 0xcccccc,
font: "monospace",
alpha: 0.85
});
menuBtn.anchor.set(0.5, 0.5);
menuBtn.x = 2048 / 2;
menuBtn.y = 1850;
overlay.addChild(menuBtn);
// Add overlay to game
overlay.zIndex = 10001;
game.addChild(overlay);
// Faint ambient noise (reuse jumpscare sound at low volume)
var jumpscareSound = LK.getSound('jumpscare');
if (jumpscareSound && jumpscareSound.setVolume) {
jumpscareSound.setVolume(0.12);
jumpscareSound.play();
}
// Button logic
retryBtn.interactive = true;
retryBtn.down = function () {
// Remove overlay and restart game
overlay.destroy();
LK.showGameOver(); // Triggers game reset
};
menuBtn.interactive = true;
menuBtn.down = function () {
overlay.destroy();
LK.showGameOver(); // Let LK engine handle main menu
};
}, 900);
}
return;
}
// Collect talismans
for (var i = talismans.length - 1; i >= 0; i--) {
var t = talismans[i];
if (!t.collected && survivor.intersects(t)) {
t.collected = true;
LK.getSound('talisman').play();
talismanScore++;
talismanTxt.setText('Talismans: ' + talismanScore);
tween(t, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
t.destroy();
}
});
talismans.splice(i, 1);
}
}
// Collect letters
for (var i = letters.length - 1; i >= 0; i--) {
var l = letters[i];
if (!l.collected && survivor.intersects(l)) {
l.collected = true;
LK.getSound('collect').play();
letterScore++;
letterTxt.setText('Letters: ' + letterScore);
tween(l, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
l.destroy();
}
});
letters.splice(i, 1);
}
}
// Win condition: reach Galata Tower
if (survivor.intersects(galata)) {
LK.effects.flashScreen(0x00ffcc, 800);
LK.showYouWin();
}
// --- Play subtle background sound effect at random intervals (5-10s) ---
var backgroundSoundTimer = null;
function playBackgroundSoundLoop() {
// Play the background sound effect at low volume
var bgSound = LK.getSound('background');
if (bgSound && bgSound.setVolume) {
bgSound.setVolume(0.18); // subtle, blends with ambient
}
if (bgSound) {
bgSound.play();
}
// Schedule next play at a random interval between 5-10 seconds
var nextDelay = 5000 + Math.floor(Math.random() * 5000); // ms
backgroundSoundTimer = LK.setTimeout(playBackgroundSoundLoop, nextDelay);
}
// Start the background sound loop
playBackgroundSoundLoop();
// Timer
if (LK.ticks % 60 === 0 && timeLeft > 0 && !jumpscareActive) {
timeLeft--;
var min = Math.floor(timeLeft / 60);
var sec = timeLeft % 60;
timerTxt.setText((min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec);
if (timeLeft === 0) {
// Time up: game over
LK.effects.flashScreen(0x000000, 1200);
LK.setTimeout(function () {
LK.showGameOver();
}, 1200);
}
}
};
// Center GUI elements
talismanTxt.x = LK.gui.top.width / 2;
letterTxt.x = LK.gui.top.width / 2;
timerTxt.x = LK.gui.top.width / 2;
Create a pixel-art monster character inspired by Turkish folklore, specifically "Gulyabani" – a tall, lanky, ghostly creature with pale, bluish-gray skin, long tangled hair covering parts of its face, and glowing red eyes. It should wear torn Ottoman-style rags, with long sharp fingers and a hunched back. Its movement should look floaty and eerie. Style should match 2D retro horror games. Transparent background PNG, facing left side, idle stance.. In-Game asset. 2d. High contrast. No shadows
Create a pixel-art character sprite of "Karabasan" – a dark, shadowy figure from Turkish folklore known to sit on people’s chests during sleep. The character should be faceless or have a barely visible distorted face with empty glowing white eyes. Its body should appear as a mass of smoky darkness, loosely humanoid, with long arms, claw-like hands, and ragged shadowy edges. It should look like it’s floating slightly above ground. Retro horror 2D game style. PNG format with transparent background, idle position facing forward.. In-Game asset. 2d. High contrast. No shadows
Create a pixel-art environment asset of the Galata Tower at night, in a dark and eerie style suitable for a 2D horror survival game. The tower should be tall and imposing with its recognizable cylindrical stone structure and conical roof. Add cracks, overgrown vines, and dim glowing windows to give it a haunted, ancient look. A full moon and cloudy night sky in the background. Mist or fog at the base. The overall atmosphere should feel ominous and mysterious. PNG format with transparent background.. In-Game asset. 2d. High contrast. No shadows
Create a pixel-art ghostly character based on the Turkish folklore concept of a "cin" (djinn). The character should have a semi-transparent, smoky body with ethereal, shifting edges. Its face should be menacing, with glowing green or blue eyes and a faint, wicked smile. Add small, broken chains or mystical symbols faintly visible on its body to suggest it was once sealed. It should be levitating, with ghost-like movement and a cursed aura. The color palette should include shades of dark blue, purple, and gray. 2D horror game style, PNG with transparent background, idle or float animation frame.. In-Game asset. 2d. High contrast. No shadows
Create a pixel-art item asset of an ancient talisman inspired by Turkish folklore. It should appear as a folded parchment wrapped in an old red thread, with faint Ottoman-style calligraphy or mystical symbols visible. The talisman should have a glowing aura or faint sparkles to imply magical power. Optional details: slightly burned edges, aged paper texture, and traces of dried wax seal. The style should be mysterious, enchanted, and suitable for a 2D horror game. PNG format with transparent background, top-down or item-inventory view.. In-Game asset. 2d. High contrast. No shadows
Create a pixel-art item of an old, mysterious letter for a horror-themed 2D game. The letter should be a yellowed, slightly torn piece of parchment with faded ink handwriting on it. Include smudges, old blood stains, or water damage for a haunting effect. The handwriting should look rushed or distressed. Fold marks or a wax seal (partially broken) can be added as details. It should look like something found in an abandoned house or crypt. PNG format with transparent background, suitable for inventory or collectible items.. In-Game asset. 2d. High contrast. No shadows
Create a pixel-art main character sprite of a scared blonde woman for a 2D horror survival game. She should have light blonde hair (shoulder length or tied in a ponytail), wide anxious eyes, and a pale complexion. Her outfit should be casual: a light-colored shirt or hoodie, jeans, and sneakers. The character should appear visibly nervous or frightened – slightly hunched posture, hands close to body, maybe holding a flashlight. Include subtle animation or idle pose showing fear. Style should match retro horror games. PNG with transparent background, front-facing idle stance.. In-Game asset. 2d. High contrast. No shadows