/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Chest = Container.expand(function (containsKey) { var self = Container.call(this); var chestGraphics = self.attachAsset('cofre', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); self.collected = false; self.containsKey = containsKey || false; self.batteryAmount = 50; self.collect = function () { if (!self.collected) { self.collected = true; self.visible = false; if (self.containsKey) { // Only allow key collection if player doesn't already have a key if (player.keyCount === 0) { player.keyCount++; LK.setScore(LK.getScore() + 10); } else { // Drop key on the ground if player already has one var droppedKey = game.addChild(new Key()); droppedKey.x = self.x; droppedKey.y = self.y; keyElements.push(droppedKey); } } else { player.batteryLevel = Math.min(100, player.batteryLevel + self.batteryAmount); LK.setScore(LK.getScore() + 15); } } }; return self; }); var Child = Container.expand(function (childName) { var self = Container.call(this); var childGraphics = self.attachAsset('child', { anchorX: 0.5, anchorY: 0.5 }); self.name = childName || 'Child'; self.isRescued = false; self.detectionRadius = 80; var nameText = new Text2(self.name, { size: 24, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 1); nameText.y = -40; self.addChild(nameText); self.rescue = function () { if (!self.isRescued && player.keyCount > 0) { self.isRescued = true; childGraphics.tint = 0x90EE90; LK.getSound('childFound').play(); LK.setScore(LK.getScore() + 25); player.keyCount--; rescuedChildren++; // Make stag run to player when child is rescued and increase its speed if (stag) { stag.isHunting = true; stag.huntingTarget = player; stag.speed += 0.5; // Increase stag speed when child is rescued } // Make player slower for 3 seconds if (player) { var originalSpeed = player.speed; player.speed = originalSpeed * 0.5; // Reduce speed to half tween(player, { speed: originalSpeed }, { duration: 3000 }); } checkWinCondition(); } }; return self; }); var Fog = Container.expand(function () { var self = Container.call(this); var fogGraphics = self.attachAsset('fog', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6 }); self.driftSpeed = 0.5; self.driftDirection = Math.random() * Math.PI * 2; self.update = function () { self.x += Math.cos(self.driftDirection) * self.driftSpeed; self.y += Math.sin(self.driftDirection) * self.driftSpeed; if (self.x < -150) self.x = 2200; if (self.x > 2200) self.x = -150; if (self.y < -150) self.y = 2900; if (self.y > 2900) self.y = -150; if (Math.random() < 0.01) { self.driftDirection += (Math.random() - 0.5) * 0.5; } }; return self; }); var Key = Container.expand(function () { var self = Container.call(this); var keyGraphics = self.attachAsset('sage', { anchorX: 0.5, anchorY: 0.5 }); keyGraphics.tint = 0xFFD700; // Gold color for keys self.collected = false; self.collect = function () { if (!self.collected && player.keyCount === 0) { self.collected = true; self.visible = false; player.keyCount++; LK.setScore(LK.getScore() + 10); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.isMoving = false; self.flashlightOn = false; self.batteryLevel = 100; self.sageCount = 0; self.keyCount = 0; self.isParalyzed = false; self.paralyzedTimer = 0; var flashlightBeam = self.addChild(LK.getAsset('flashlightBeam', { anchorX: 0.5, anchorY: 1.0, alpha: 0 })); self.update = function () { if (self.isParalyzed) { self.paralyzedTimer--; if (self.paralyzedTimer <= 0) { self.isParalyzed = false; playerGraphics.tint = 0xFFFFFF; } return; } if (self.flashlightOn && self.batteryLevel > 0) { self.batteryLevel -= 0.2; flashlightBeam.alpha = Math.min(1, self.batteryLevel / 100); flashlightBeam.y = -20; } else { flashlightBeam.alpha = 0; } if (self.batteryLevel <= 0) { self.flashlightOn = false; } }; self.toggleFlashlight = function () { if (self.batteryLevel > 0) { self.flashlightOn = !self.flashlightOn; LK.getSound('flashlightClick').play(); } }; self.paralyze = function () { self.isParalyzed = true; self.paralyzedTimer = 180; playerGraphics.tint = 0x666666; }; self.die = function () { // Reset game state resetGame(); }; return self; }); var Sage = Container.expand(function () { var self = Container.call(this); var sageGraphics = self.attachAsset('sage', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.collect = function () { if (!self.collected) { self.collected = true; self.visible = false; player.sageCount++; LK.setScore(LK.getScore() + 5); } }; return self; }); var Stag = Container.expand(function () { var self = Container.call(this); var assetId = difficulty === 'hardcore' ? 'hardcore' : 'stag'; var stagGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Set speed based on difficulty if (difficulty === 'easy') { self.speed = 1.5; } else if (difficulty === 'medium') { self.speed = 2.0; } else if (difficulty === 'hardcore') { self.speed = 2.5; } self.detectionRadius = 250; self.attackRadius = 100; self.isHunting = false; self.huntingTarget = null; self.chainsTimer = 0; self.isStunned = false; self.stunnedTimer = 0; self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderSpeed = 0.8; self.caveTimer = 300; // Time before emerging from cave (5 seconds) self.hasEmerged = false; self.update = function () { // Handle cave emergence if (!self.hasEmerged) { self.caveTimer--; if (self.caveTimer <= 0) { self.hasEmerged = true; // Move towards center of map when emerging self.wanderDirection = Math.atan2(1366 - self.y, 1024 - self.x); // Direction towards center } return; // Stay in cave until timer expires } if (self.isStunned) { self.stunnedTimer--; if (self.stunnedTimer <= 0) { self.isStunned = false; stagGraphics.alpha = 1; } return; } self.chainsTimer++; if (self.chainsTimer >= 300) { LK.getSound('stagChains').play(); self.chainsTimer = 0; } if (player && !player.isParalyzed) { var distanceToPlayer = Math.sqrt(Math.pow(self.x - player.x, 2) + Math.pow(self.y - player.y, 2)); if (player.flashlightOn && distanceToPlayer < 200) { self.stun(); return; } if (distanceToPlayer < self.detectionRadius) { self.isHunting = true; self.huntingTarget = player; } if (self.isHunting && self.huntingTarget) { var dx = self.huntingTarget.x - self.x; var dy = self.huntingTarget.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } if (distance < self.attackRadius) { // Check if player is inside a wardrobe before attacking var playerInWardrobe = false; var playerWardrobe = null; for (var w = 0; w < wardrobeElements.length; w++) { if (wardrobeElements[w].playerInside) { playerInWardrobe = true; playerWardrobe = wardrobeElements[w]; break; } } // In hardcore mode, stag waits at the wardrobe when player enters if (playerInWardrobe && difficulty === 'hardcore' && playerWardrobe) { // Move stag to the wardrobe and wait there var wardrobeDx = playerWardrobe.x - self.x; var wardrobeDy = playerWardrobe.y - self.y; var wardrobeDistance = Math.sqrt(wardrobeDx * wardrobeDx + wardrobeDy * wardrobeDy); if (wardrobeDistance > 50) { // Move towards the wardrobe self.x += wardrobeDx / wardrobeDistance * self.speed; self.y += wardrobeDy / wardrobeDistance * self.speed; } // Stop hunting while waiting at wardrobe return; } // Only attack if player is not in a wardrobe if (!playerInWardrobe) { self.huntingTarget.die(); self.isHunting = false; self.huntingTarget = null; } } } else { // Wander around the map when not hunting self.wanderTimer++; if (self.wanderTimer >= 120) { self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; } self.x += Math.cos(self.wanderDirection) * self.wanderSpeed; self.y += Math.sin(self.wanderDirection) * self.wanderSpeed; // Keep stag within map bounds if (self.x < 50) { self.x = 50; self.wanderDirection = Math.random() * Math.PI * 2; } if (self.x > 1998) { self.x = 1998; self.wanderDirection = Math.random() * Math.PI * 2; } if (self.y < 50) { self.y = 50; self.wanderDirection = Math.random() * Math.PI * 2; } if (self.y > 2682) { self.y = 2682; self.wanderDirection = Math.random() * Math.PI * 2; } } } }; self.stun = function () { self.isStunned = true; self.stunnedTimer = 120; self.isHunting = false; self.huntingTarget = null; stagGraphics.alpha = 0.5; }; return self; }); var Wardrobe = Container.expand(function () { var self = Container.call(this); var wardrobeGraphics = self.attachAsset('armario', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); self.playerInside = false; self.detectionRadius = 80; self.enterWardrobe = function () { if (!self.playerInside) { self.playerInside = true; player.visible = false; stag.isHunting = false; stag.huntingTarget = null; } }; self.exitWardrobe = function () { if (self.playerInside) { self.playerInside = false; player.visible = true; // In hardcore mode, make stag leave the wardrobe area when player exits if (difficulty === 'hardcore' && stag) { stag.isHunting = false; stag.huntingTarget = null; // Make stag wander away from the wardrobe stag.wanderDirection = Math.atan2(stag.y - self.y, stag.x - self.x); // Direction away from wardrobe stag.wanderTimer = 0; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0d1b0d }); /**** * Game Code ****/ // Game variables // Initialize game assets var player; var stag; var children = []; var fogElements = []; var sageElements = []; var environments = []; var keyElements = []; var chestElements = []; var wardrobeElements = []; var rescuedChildren = 0; var lastTapTime = 0; var tapCount = 0; var tripleTapCount = 0; var lastTripleTapTime = 0; var gameNight = 1; var isGameActive = true; var dayProgress = 0; var gameState = 'menu'; // 'menu' or 'playing' var difficulty = 'easy'; // 'easy', 'medium', 'hardcore' var currentLanguage = storage.language || 'english'; // 'english' or 'spanish' var requiredChildren = 4; // Changes based on difficulty var maxWardrobes = 4; // Changes based on difficulty // UI elements var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.visible = false; LK.gui.topRight.addChild(scoreText); var nightText = new Text2('Night: 1/99', { size: 40, fill: 0xFFFFFF }); nightText.anchor.set(0.5, 0); nightText.visible = false; LK.gui.top.addChild(nightText); var batteryText = new Text2('Battery: 100%', { size: 30, fill: 0xFFFF00 }); batteryText.anchor.set(0, 0); batteryText.x = 20; batteryText.y = 80; batteryText.visible = false; LK.gui.topLeft.addChild(batteryText); var sageText = new Text2('Sage: 0', { size: 30, fill: 0x90EE90 }); sageText.anchor.set(0, 0); sageText.x = 20; sageText.y = 120; sageText.visible = false; LK.gui.topLeft.addChild(sageText); var keyText = new Text2('Keys: 0', { size: 30, fill: 0xFFD700 }); keyText.anchor.set(0, 0); keyText.x = 20; keyText.y = 160; keyText.visible = false; LK.gui.topLeft.addChild(keyText); var menuButton = new Text2('MENU', { size: 30, fill: 0xFFFFFF }); menuButton.anchor.set(1, 0); menuButton.x = -20; menuButton.y = 20; menuButton.visible = false; LK.gui.topRight.addChild(menuButton); var instructionText = new Text2('Tap to move. Triple tap near wardrobe to hide.', { size: 24, fill: 0xCCCCCC }); instructionText.anchor.set(0.5, 1); instructionText.visible = false; LK.gui.bottom.addChild(instructionText); // Create flashlight button var flashlightButton = new Text2('š”', { size: 60, fill: 0xFFFF00 }); flashlightButton.anchor.set(1, 1); flashlightButton.x = -20; flashlightButton.y = -20; flashlightButton.visible = false; LK.gui.bottomRight.addChild(flashlightButton); // Menu UI elements var menuImage = LK.getAsset('menu', { anchorX: 0.5, anchorY: 0.5 }); menuImage.y = -150; LK.gui.center.addChild(menuImage); var titleText = new Text2('99 nights in the forest', { size: 80, fill: 0x90EE90 }); titleText.anchor.set(0.5, 0.5); LK.gui.center.addChild(titleText); var playButton = new Text2('PLAY', { size: 50, fill: 0xFFFFFF }); playButton.anchor.set(0.5, 0.5); playButton.y = 100; LK.gui.center.addChild(playButton); var difficultyText = new Text2('EASY', { size: 40, fill: 0x90EE90 }); difficultyText.anchor.set(0.5, 0.5); difficultyText.y = 50; LK.gui.center.addChild(difficultyText); var leftArrow = new Text2('<', { size: 60, fill: 0xFFFFFF }); leftArrow.anchor.set(0.5, 0.5); leftArrow.x = -120; leftArrow.y = 50; LK.gui.center.addChild(leftArrow); var rightArrow = new Text2('>', { size: 60, fill: 0xFFFFFF }); rightArrow.anchor.set(0.5, 0.5); rightArrow.x = 120; rightArrow.y = 50; LK.gui.center.addChild(rightArrow); var menuInstructions = new Text2('Rescue 4 children from the forest\nAvoid the stag or hide in wardrobes\nUse your flashlight to stun the stag', { size: 30, fill: 0xCCCCCC }); menuInstructions.anchor.set(0.5, 0.5); menuInstructions.y = 180; LK.gui.center.addChild(menuInstructions); var tutorialButton = new Text2('TUTORIAL', { size: 40, fill: 0x90EE90 }); tutorialButton.anchor.set(0.5, 0.5); tutorialButton.y = 280; LK.gui.center.addChild(tutorialButton); var languageButton = new Text2('ENGLISH', { size: 40, fill: 0x90EE90 }); languageButton.anchor.set(0.5, 0.5); languageButton.y = 320; LK.gui.center.addChild(languageButton); var tutorialText = new Text2('CONTROLS:\n⢠Tap anywhere to move\n⢠Triple tap near wardrobe to hide\n⢠Use flashlight button to stun stag\n⢠Collect keys from chests\n⢠Find and rescue children\n\nHOW TO PLAY:\n⢠Rescue all children to win\n⢠Avoid the stag or hide in wardrobes\n⢠Use flashlight wisely - limited battery\n⢠Collect sage for extra points\n⢠Game gets harder each night', { size: 26, fill: 0xFFFFFF }); tutorialText.anchor.set(0.5, 0.5); tutorialText.y = 0; tutorialText.visible = false; LK.gui.center.addChild(tutorialText); var backButton = new Text2('BACK', { size: 40, fill: 0xFF6666 }); backButton.anchor.set(0.5, 0.5); backButton.y = 250; backButton.visible = false; LK.gui.center.addChild(backButton); // Flashlight button event handlers flashlightButton.down = function (x, y, obj) { if (player && player.batteryLevel > 0) { player.toggleFlashlight(); } }; // Difficulty selection event handlers leftArrow.down = function (x, y, obj) { if (difficulty === 'medium') { difficulty = 'easy'; difficultyText.tint = 0x90EE90; } else if (difficulty === 'hardcore') { difficulty = 'medium'; difficultyText.tint = 0xFFAA00; } updateAllTexts(); }; rightArrow.down = function (x, y, obj) { if (difficulty === 'easy') { difficulty = 'medium'; difficultyText.tint = 0xFFAA00; } else if (difficulty === 'medium') { difficulty = 'hardcore'; difficultyText.tint = 0xFF0000; } updateAllTexts(); }; // Tutorial button event handler tutorialButton.down = function (x, y, obj) { // Hide menu elements menuImage.visible = false; titleText.visible = false; playButton.visible = false; tutorialButton.visible = false; languageButton.visible = false; menuInstructions.visible = false; difficultyText.visible = false; leftArrow.visible = false; rightArrow.visible = false; // Show tutorial elements tutorialText.visible = true; backButton.visible = true; }; // Back button event handler backButton.down = function (x, y, obj) { // Hide tutorial elements tutorialText.visible = false; backButton.visible = false; // Show menu elements menuImage.visible = true; titleText.visible = true; playButton.visible = true; tutorialButton.visible = true; languageButton.visible = true; menuInstructions.visible = true; difficultyText.visible = true; leftArrow.visible = true; rightArrow.visible = true; }; // Language button event handler languageButton.down = function (x, y, obj) { if (currentLanguage === 'english') { currentLanguage = 'spanish'; } else { currentLanguage = 'english'; } storage.language = currentLanguage; updateAllTexts(); }; // Function to update all text elements based on current language function updateAllTexts() { var texts = getTexts(); if (currentLanguage === 'spanish') { // Update to Spanish titleText.setText(texts.spanish.title); playButton.setText(texts.spanish.play); tutorialButton.setText(texts.spanish.tutorial); languageButton.setText(texts.spanish.language); backButton.setText(texts.spanish.back); tutorialText.setText(texts.spanish.tutorialContent); instructionText.setText(texts.spanish.instructions); // Update difficulty text if (difficulty === 'easy') { difficultyText.setText(texts.spanish.easy); menuInstructions.setText(texts.spanish.easyDesc); } else if (difficulty === 'medium') { difficultyText.setText(texts.spanish.medium); menuInstructions.setText(texts.spanish.mediumDesc); } else if (difficulty === 'hardcore') { difficultyText.setText(texts.spanish.hardcore); menuInstructions.setText(texts.spanish.hardcoreDesc); } } else { // Update to English titleText.setText(texts.english.title); playButton.setText(texts.english.play); tutorialButton.setText(texts.english.tutorial); languageButton.setText(texts.english.language); backButton.setText(texts.english.back); tutorialText.setText(texts.english.tutorialContent); instructionText.setText(texts.english.instructions); // Update difficulty text if (difficulty === 'easy') { difficultyText.setText(texts.english.easy); menuInstructions.setText(texts.english.easyDesc); } else if (difficulty === 'medium') { difficultyText.setText(texts.english.medium); menuInstructions.setText(texts.english.mediumDesc); } else if (difficulty === 'hardcore') { difficultyText.setText(texts.english.hardcore); menuInstructions.setText(texts.english.hardcoreDesc); } } } // Function that returns all text translations function getTexts() { return { english: { title: '99 nights in the forest', play: 'PLAY', tutorial: 'TUTORIAL', language: 'ENGLISH', back: 'BACK', tutorialContent: 'CONTROLS:\n⢠Tap anywhere to move\n⢠Triple tap near wardrobe to hide\n⢠Use flashlight button to stun stag\n⢠Collect keys from chests\n⢠Find and rescue children\n\nHOW TO PLAY:\n⢠Rescue all children to win\n⢠Avoid the stag or hide in wardrobes\n⢠Use flashlight wisely - limited battery\n⢠Collect sage for extra points\n⢠Game gets harder each night', instructions: 'Tap to move. Triple tap near wardrobe to hide.', easy: 'EASY', medium: 'MEDIUM', hardcore: 'HARDCORE', easyDesc: 'Rescue 4 children from the forest\nAvoid the stag or hide in wardrobes\nUse your flashlight to stun the stag', mediumDesc: 'Rescue 6 children from the forest\nStag moves faster\nAvoid the stag or hide in wardrobes', hardcoreDesc: 'Rescue 10 children from the forest\nOnly 2 wardrobes available\nStag moves very fast', score: 'Score: ', night: 'Night: ', battery: 'Battery: ', sage: 'Sage: ', keys: 'Keys: ', menu: 'MENU' }, spanish: { title: '99 noches en el bosque', play: 'JUGAR', tutorial: 'TUTORIAL', language: 'ESPAĆOL', back: 'VOLVER', tutorialContent: 'CONTROLES:\n⢠Toca en cualquier lugar para moverte\n⢠Triple toque cerca del armario para esconderte\n⢠Usa el botón de linterna para aturdir al ciervo\n⢠Recoge llaves de los cofres\n⢠Encuentra y rescata a los niƱos\n\nCĆMO JUGAR:\n⢠Rescata a todos los niƱos para ganar\n⢠Evita al ciervo o escóndete en armarios\n⢠Usa la linterna sabiamente - baterĆa limitada\n⢠Recoge salvia para puntos extra\n⢠El juego se vuelve mĆ”s difĆcil cada noche', instructions: 'Toca para moverte. Triple toque cerca del armario para esconderte.', easy: 'FĆCIL', medium: 'MEDIO', hardcore: 'EXTREMO', easyDesc: 'Rescata 4 niƱos del bosque\nEvita al ciervo o escóndete en armarios\nUsa tu linterna para aturdir al ciervo', mediumDesc: 'Rescata 6 niƱos del bosque\nEl ciervo se mueve mĆ”s rĆ”pido\nEvita al ciervo o escóndete en armarios', hardcoreDesc: 'Rescata 10 niƱos del bosque\nSolo 2 armarios disponibles\nEl ciervo se mueve muy rĆ”pido', score: 'Puntuación: ', night: 'Noche: ', battery: 'BaterĆa: ', sage: 'Salvia: ', keys: 'Llaves: ', menu: 'MENĆ' } }; } // Play button event handler playButton.down = function (x, y, obj) { startGame(); }; // Menu button event handler menuButton.down = function (x, y, obj) { resetGame(); }; // Start game function function startGame() { gameState = 'playing'; // Set difficulty parameters if (difficulty === 'easy') { requiredChildren = 4; maxWardrobes = 4; } else if (difficulty === 'medium') { requiredChildren = 6; maxWardrobes = 4; } else if (difficulty === 'hardcore') { requiredChildren = 10; maxWardrobes = 2; } // Hide menu UI menuImage.visible = false; titleText.visible = false; playButton.visible = false; tutorialButton.visible = false; languageButton.visible = false; menuInstructions.visible = false; difficultyText.visible = false; leftArrow.visible = false; rightArrow.visible = false; // Show game UI scoreText.visible = true; nightText.visible = true; batteryText.visible = true; sageText.visible = true; keyText.visible = true; instructionText.visible = true; flashlightButton.visible = true; menuButton.visible = true; // Update menu button text var texts = getTexts(); var currentTexts = currentLanguage === 'spanish' ? texts.spanish : texts.english; menuButton.setText(currentTexts.menu); // Initialize game elements initializeGame(); // Start ambient music LK.playMusic('forestAmbient'); } // Initialize game elements function initializeGame() { // Create player player = game.addChild(new Player()); player.x = 1024; player.y = 2000; // Create stag inside the cave stag = game.addChild(new Stag()); stag.x = 1200; // Cave x position stag.y = 1800; // Cave y position // Create environments var church = game.addChild(LK.getAsset('church', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 400 })); environments.push(church); var village = game.addChild(LK.getAsset('village', { anchorX: 0.5, anchorY: 0.5, x: 1600, y: 600 })); environments.push(village); var cave = game.addChild(LK.getAsset('cave', { anchorX: 0.5, anchorY: 0.5, x: 1200, y: 1800 })); environments.push(cave); // Create trees for (var i = 0; i < 15; i++) { var tree = game.addChild(LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: Math.random() * 2732 })); environments.push(tree); } // Create children to rescue based on difficulty var childNames = ['Luna', 'TomĆ”s', 'Ana', 'Leo', 'Sofia', 'Diego', 'Emma', 'Carlos', 'Mia', 'Alex']; var childPositions = [{ x: 350, y: 450 }, { x: 1650, y: 650 }, { x: 1250, y: 1850 }, { x: 800, y: 1200 }, { x: 400, y: 1600 }, { x: 1700, y: 1400 }, { x: 600, y: 800 }, { x: 1400, y: 1800 }, { x: 900, y: 500 }, { x: 1100, y: 2200 }]; for (var j = 0; j < requiredChildren; j++) { var child = game.addChild(new Child(childNames[j])); child.x = childPositions[j].x; child.y = childPositions[j].y; children.push(child); } // Create fog for (var k = 0; k < 12; k++) { var fog = game.addChild(new Fog()); fog.x = Math.random() * 2048; fog.y = Math.random() * 2732; fogElements.push(fog); } // Create sage items for (var l = 0; l < 8; l++) { var sage = game.addChild(new Sage()); sage.x = Math.random() * 1800 + 100; sage.y = Math.random() * 2400 + 100; sageElements.push(sage); } // Create chests - number of keys based on difficulty var chestTypes = []; var keyCount = requiredChildren; // Keys match required children count var batteryCount = 10 - keyCount; // Remaining chests have batteries // Add chests with keys based on difficulty for (var k = 0; k < keyCount; k++) { chestTypes.push(true); } // Add chests with batteries for (var b = 0; b < batteryCount; b++) { chestTypes.push(false); } // Shuffle the array to randomize positions for (var shuffle = chestTypes.length - 1; shuffle > 0; shuffle--) { var randomIndex = Math.floor(Math.random() * (shuffle + 1)); var temp = chestTypes[shuffle]; chestTypes[shuffle] = chestTypes[randomIndex]; chestTypes[randomIndex] = temp; } // Create the 10 chests with predetermined types for (var m = 0; m < 10; m++) { var containsKey = chestTypes[m]; var chest = game.addChild(new Chest(containsKey)); chest.x = Math.random() * 1800 + 100; chest.y = Math.random() * 2400 + 100; chestElements.push(chest); } // Create wardrobes for hiding based on difficulty var wardrobePositions = [{ x: 500, y: 800 }, { x: 1500, y: 1200 }, { x: 800, y: 2000 }, { x: 1800, y: 400 }]; for (var n = 0; n < maxWardrobes; n++) { var wardrobe = game.addChild(new Wardrobe()); wardrobe.x = wardrobePositions[n].x; wardrobe.y = wardrobePositions[n].y; wardrobeElements.push(wardrobe); } } // Movement variables var targetX = null; var targetY = null; var pressStartTime = 0; var isLongPress = false; function checkWinCondition() { if (rescuedChildren >= requiredChildren) { LK.showYouWin(); } } function checkLoseCondition() { // No lose condition - reaching 99 days is now winning } function resetGame() { // Reset game variables rescuedChildren = 0; gameNight = 1; isGameActive = true; dayProgress = 0; gameState = 'menu'; // Clear existing game objects for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; game.removeChild(child); if (child.destroy) { child.destroy(); } } // Clear arrays children.length = 0; fogElements.length = 0; sageElements.length = 0; keyElements.length = 0; chestElements.length = 0; wardrobeElements.length = 0; environments.length = 0; // Reset score LK.setScore(0); // Hide tutorial elements tutorialText.visible = false; backButton.visible = false; // Show menu UI menuImage.visible = true; titleText.visible = true; playButton.visible = true; tutorialButton.visible = true; languageButton.visible = true; menuInstructions.visible = true; difficultyText.visible = true; leftArrow.visible = true; rightArrow.visible = true; // Initialize texts based on current language updateAllTexts(); // Hide game UI scoreText.visible = false; nightText.visible = false; batteryText.visible = false; sageText.visible = false; keyText.visible = false; instructionText.visible = false; flashlightButton.visible = false; menuButton.visible = false; // Stop music LK.stopMusic(); } // Event handlers game.down = function (x, y, obj) { if (gameState !== 'playing') return; pressStartTime = LK.ticks; isLongPress = false; targetX = x; targetY = y; }; game.up = function (x, y, obj) { if (gameState !== 'playing') return; var currentTime = LK.ticks; var pressDuration = currentTime - pressStartTime; if (pressDuration <= 30 && !isLongPress) { // Check for triple tap first if (currentTime - lastTripleTapTime < 45) { tripleTapCount++; if (tripleTapCount === 3) { // Triple tap detected - check for wardrobe interaction var nearWardrobe = null; for (var i = 0; i < wardrobeElements.length; i++) { var wardrobe = wardrobeElements[i]; var distanceToWardrobe = Math.sqrt(Math.pow(player.x - wardrobe.x, 2) + Math.pow(player.y - wardrobe.y, 2)); if (distanceToWardrobe < wardrobe.detectionRadius) { nearWardrobe = wardrobe; break; } } if (nearWardrobe) { if (nearWardrobe.playerInside) { nearWardrobe.exitWardrobe(); } else { nearWardrobe.enterWardrobe(); } } tripleTapCount = 0; return; } } else { tripleTapCount = 1; } lastTripleTapTime = currentTime; // Set movement target targetX = x; targetY = y; } }; game.move = function (x, y, obj) { if (gameState !== 'playing') return; if (!isLongPress && LK.ticks - pressStartTime < 30) { targetX = x; targetY = y; } }; // Main game update loop game.update = function () { if (gameState !== 'playing' || !isGameActive) return; // Move player toward target if (targetX !== null && targetY !== null && !player.isParalyzed) { var dx = targetX - player.x; var dy = targetY - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { player.x += dx / distance * player.speed; player.y += dy / distance * player.speed; player.isMoving = true; if (LK.ticks % 30 === 0) { LK.getSound('footstep').play(); } } else { player.isMoving = false; targetX = null; targetY = null; } } // Keep player in bounds player.x = Math.max(50, Math.min(1998, player.x)); player.y = Math.max(50, Math.min(2682, player.y)); // Check child rescue interactions for (var i = 0; i < children.length; i++) { var child = children[i]; if (!child.isRescued) { var distanceToChild = Math.sqrt(Math.pow(player.x - child.x, 2) + Math.pow(player.y - child.y, 2)); if (distanceToChild < child.detectionRadius) { child.rescue(); } } } // Check sage collection for (var j = 0; j < sageElements.length; j++) { var sage = sageElements[j]; if (!sage.collected) { var distanceToSage = Math.sqrt(Math.pow(player.x - sage.x, 2) + Math.pow(player.y - sage.y, 2)); if (distanceToSage < 40) { sage.collect(); } } } // Check key collection for (var k = 0; k < keyElements.length; k++) { var key = keyElements[k]; if (!key.collected) { var distanceToKey = Math.sqrt(Math.pow(player.x - key.x, 2) + Math.pow(player.y - key.y, 2)); if (distanceToKey < 40) { key.collect(); } } } // Check chest collection for (var l = 0; l < chestElements.length; l++) { var chest = chestElements[l]; if (!chest.collected) { var distanceToChest = Math.sqrt(Math.pow(player.x - chest.x, 2) + Math.pow(player.y - chest.y, 2)); if (distanceToChest < 40) { chest.collect(); } } } // Advance night every 30 seconds (1800 ticks) with acceleration based on rescued children var dayAcceleration = rescuedChildren + 1; // 1x speed with 0 children, 2x with 1 child, etc. // Add acceleration to day progress dayProgress += dayAcceleration; // Check if a full day has passed (1800 ticks worth of progress) if (dayProgress >= 1800) { gameNight++; stag.speed += 0.1; dayProgress -= 1800; // Reset progress but keep overflow checkWinCondition(); // Check win condition instead of lose } // Check if player is inside a wardrobe and prevent movement var playerInWardrobe = false; for (var w = 0; w < wardrobeElements.length; w++) { if (wardrobeElements[w].playerInside) { playerInWardrobe = true; targetX = null; targetY = null; break; } } // Reset tap count if too much time has passed if (LK.ticks - lastTapTime > 30) { tapCount = 0; } // Reset triple tap count if too much time has passed if (LK.ticks - lastTripleTapTime > 45) { tripleTapCount = 0; } // Update UI var texts = getTexts(); var currentTexts = currentLanguage === 'spanish' ? texts.spanish : texts.english; scoreText.setText(currentTexts.score + LK.getScore()); nightText.setText(currentTexts.night + gameNight + '/99'); batteryText.setText(currentTexts.battery + Math.ceil(player.batteryLevel) + '%'); sageText.setText(currentTexts.sage + player.sageCount); keyText.setText(currentTexts.keys + player.keyCount); // Battery color warning if (player.batteryLevel < 25) { batteryText.tint = 0xFF0000; } else if (player.batteryLevel < 50) { batteryText.tint = 0xFFAA00; } else { batteryText.tint = 0xFFFF00; } // Update flashlight button appearance if (player.flashlightOn) { flashlightButton.tint = 0xFFFFFF; // Bright when on flashlightButton.alpha = 1.0; } else { flashlightButton.tint = 0x888888; // Dim when off flashlightButton.alpha = 0.6; } // Disable button when no battery if (player.batteryLevel <= 0) { flashlightButton.tint = 0x444444; flashlightButton.alpha = 0.3; } }; // Game starts in menu state - no automatic initialization
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Chest = Container.expand(function (containsKey) {
var self = Container.call(this);
var chestGraphics = self.attachAsset('cofre', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.collected = false;
self.containsKey = containsKey || false;
self.batteryAmount = 50;
self.collect = function () {
if (!self.collected) {
self.collected = true;
self.visible = false;
if (self.containsKey) {
// Only allow key collection if player doesn't already have a key
if (player.keyCount === 0) {
player.keyCount++;
LK.setScore(LK.getScore() + 10);
} else {
// Drop key on the ground if player already has one
var droppedKey = game.addChild(new Key());
droppedKey.x = self.x;
droppedKey.y = self.y;
keyElements.push(droppedKey);
}
} else {
player.batteryLevel = Math.min(100, player.batteryLevel + self.batteryAmount);
LK.setScore(LK.getScore() + 15);
}
}
};
return self;
});
var Child = Container.expand(function (childName) {
var self = Container.call(this);
var childGraphics = self.attachAsset('child', {
anchorX: 0.5,
anchorY: 0.5
});
self.name = childName || 'Child';
self.isRescued = false;
self.detectionRadius = 80;
var nameText = new Text2(self.name, {
size: 24,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 1);
nameText.y = -40;
self.addChild(nameText);
self.rescue = function () {
if (!self.isRescued && player.keyCount > 0) {
self.isRescued = true;
childGraphics.tint = 0x90EE90;
LK.getSound('childFound').play();
LK.setScore(LK.getScore() + 25);
player.keyCount--;
rescuedChildren++;
// Make stag run to player when child is rescued and increase its speed
if (stag) {
stag.isHunting = true;
stag.huntingTarget = player;
stag.speed += 0.5; // Increase stag speed when child is rescued
}
// Make player slower for 3 seconds
if (player) {
var originalSpeed = player.speed;
player.speed = originalSpeed * 0.5; // Reduce speed to half
tween(player, {
speed: originalSpeed
}, {
duration: 3000
});
}
checkWinCondition();
}
};
return self;
});
var Fog = Container.expand(function () {
var self = Container.call(this);
var fogGraphics = self.attachAsset('fog', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
self.driftSpeed = 0.5;
self.driftDirection = Math.random() * Math.PI * 2;
self.update = function () {
self.x += Math.cos(self.driftDirection) * self.driftSpeed;
self.y += Math.sin(self.driftDirection) * self.driftSpeed;
if (self.x < -150) self.x = 2200;
if (self.x > 2200) self.x = -150;
if (self.y < -150) self.y = 2900;
if (self.y > 2900) self.y = -150;
if (Math.random() < 0.01) {
self.driftDirection += (Math.random() - 0.5) * 0.5;
}
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
var keyGraphics = self.attachAsset('sage', {
anchorX: 0.5,
anchorY: 0.5
});
keyGraphics.tint = 0xFFD700; // Gold color for keys
self.collected = false;
self.collect = function () {
if (!self.collected && player.keyCount === 0) {
self.collected = true;
self.visible = false;
player.keyCount++;
LK.setScore(LK.getScore() + 10);
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.isMoving = false;
self.flashlightOn = false;
self.batteryLevel = 100;
self.sageCount = 0;
self.keyCount = 0;
self.isParalyzed = false;
self.paralyzedTimer = 0;
var flashlightBeam = self.addChild(LK.getAsset('flashlightBeam', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0
}));
self.update = function () {
if (self.isParalyzed) {
self.paralyzedTimer--;
if (self.paralyzedTimer <= 0) {
self.isParalyzed = false;
playerGraphics.tint = 0xFFFFFF;
}
return;
}
if (self.flashlightOn && self.batteryLevel > 0) {
self.batteryLevel -= 0.2;
flashlightBeam.alpha = Math.min(1, self.batteryLevel / 100);
flashlightBeam.y = -20;
} else {
flashlightBeam.alpha = 0;
}
if (self.batteryLevel <= 0) {
self.flashlightOn = false;
}
};
self.toggleFlashlight = function () {
if (self.batteryLevel > 0) {
self.flashlightOn = !self.flashlightOn;
LK.getSound('flashlightClick').play();
}
};
self.paralyze = function () {
self.isParalyzed = true;
self.paralyzedTimer = 180;
playerGraphics.tint = 0x666666;
};
self.die = function () {
// Reset game state
resetGame();
};
return self;
});
var Sage = Container.expand(function () {
var self = Container.call(this);
var sageGraphics = self.attachAsset('sage', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.collect = function () {
if (!self.collected) {
self.collected = true;
self.visible = false;
player.sageCount++;
LK.setScore(LK.getScore() + 5);
}
};
return self;
});
var Stag = Container.expand(function () {
var self = Container.call(this);
var assetId = difficulty === 'hardcore' ? 'hardcore' : 'stag';
var stagGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Set speed based on difficulty
if (difficulty === 'easy') {
self.speed = 1.5;
} else if (difficulty === 'medium') {
self.speed = 2.0;
} else if (difficulty === 'hardcore') {
self.speed = 2.5;
}
self.detectionRadius = 250;
self.attackRadius = 100;
self.isHunting = false;
self.huntingTarget = null;
self.chainsTimer = 0;
self.isStunned = false;
self.stunnedTimer = 0;
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderSpeed = 0.8;
self.caveTimer = 300; // Time before emerging from cave (5 seconds)
self.hasEmerged = false;
self.update = function () {
// Handle cave emergence
if (!self.hasEmerged) {
self.caveTimer--;
if (self.caveTimer <= 0) {
self.hasEmerged = true;
// Move towards center of map when emerging
self.wanderDirection = Math.atan2(1366 - self.y, 1024 - self.x); // Direction towards center
}
return; // Stay in cave until timer expires
}
if (self.isStunned) {
self.stunnedTimer--;
if (self.stunnedTimer <= 0) {
self.isStunned = false;
stagGraphics.alpha = 1;
}
return;
}
self.chainsTimer++;
if (self.chainsTimer >= 300) {
LK.getSound('stagChains').play();
self.chainsTimer = 0;
}
if (player && !player.isParalyzed) {
var distanceToPlayer = Math.sqrt(Math.pow(self.x - player.x, 2) + Math.pow(self.y - player.y, 2));
if (player.flashlightOn && distanceToPlayer < 200) {
self.stun();
return;
}
if (distanceToPlayer < self.detectionRadius) {
self.isHunting = true;
self.huntingTarget = player;
}
if (self.isHunting && self.huntingTarget) {
var dx = self.huntingTarget.x - self.x;
var dy = self.huntingTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
if (distance < self.attackRadius) {
// Check if player is inside a wardrobe before attacking
var playerInWardrobe = false;
var playerWardrobe = null;
for (var w = 0; w < wardrobeElements.length; w++) {
if (wardrobeElements[w].playerInside) {
playerInWardrobe = true;
playerWardrobe = wardrobeElements[w];
break;
}
}
// In hardcore mode, stag waits at the wardrobe when player enters
if (playerInWardrobe && difficulty === 'hardcore' && playerWardrobe) {
// Move stag to the wardrobe and wait there
var wardrobeDx = playerWardrobe.x - self.x;
var wardrobeDy = playerWardrobe.y - self.y;
var wardrobeDistance = Math.sqrt(wardrobeDx * wardrobeDx + wardrobeDy * wardrobeDy);
if (wardrobeDistance > 50) {
// Move towards the wardrobe
self.x += wardrobeDx / wardrobeDistance * self.speed;
self.y += wardrobeDy / wardrobeDistance * self.speed;
}
// Stop hunting while waiting at wardrobe
return;
}
// Only attack if player is not in a wardrobe
if (!playerInWardrobe) {
self.huntingTarget.die();
self.isHunting = false;
self.huntingTarget = null;
}
}
} else {
// Wander around the map when not hunting
self.wanderTimer++;
if (self.wanderTimer >= 120) {
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
}
self.x += Math.cos(self.wanderDirection) * self.wanderSpeed;
self.y += Math.sin(self.wanderDirection) * self.wanderSpeed;
// Keep stag within map bounds
if (self.x < 50) {
self.x = 50;
self.wanderDirection = Math.random() * Math.PI * 2;
}
if (self.x > 1998) {
self.x = 1998;
self.wanderDirection = Math.random() * Math.PI * 2;
}
if (self.y < 50) {
self.y = 50;
self.wanderDirection = Math.random() * Math.PI * 2;
}
if (self.y > 2682) {
self.y = 2682;
self.wanderDirection = Math.random() * Math.PI * 2;
}
}
}
};
self.stun = function () {
self.isStunned = true;
self.stunnedTimer = 120;
self.isHunting = false;
self.huntingTarget = null;
stagGraphics.alpha = 0.5;
};
return self;
});
var Wardrobe = Container.expand(function () {
var self = Container.call(this);
var wardrobeGraphics = self.attachAsset('armario', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
self.playerInside = false;
self.detectionRadius = 80;
self.enterWardrobe = function () {
if (!self.playerInside) {
self.playerInside = true;
player.visible = false;
stag.isHunting = false;
stag.huntingTarget = null;
}
};
self.exitWardrobe = function () {
if (self.playerInside) {
self.playerInside = false;
player.visible = true;
// In hardcore mode, make stag leave the wardrobe area when player exits
if (difficulty === 'hardcore' && stag) {
stag.isHunting = false;
stag.huntingTarget = null;
// Make stag wander away from the wardrobe
stag.wanderDirection = Math.atan2(stag.y - self.y, stag.x - self.x); // Direction away from wardrobe
stag.wanderTimer = 0;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0d1b0d
});
/****
* Game Code
****/
// Game variables
// Initialize game assets
var player;
var stag;
var children = [];
var fogElements = [];
var sageElements = [];
var environments = [];
var keyElements = [];
var chestElements = [];
var wardrobeElements = [];
var rescuedChildren = 0;
var lastTapTime = 0;
var tapCount = 0;
var tripleTapCount = 0;
var lastTripleTapTime = 0;
var gameNight = 1;
var isGameActive = true;
var dayProgress = 0;
var gameState = 'menu'; // 'menu' or 'playing'
var difficulty = 'easy'; // 'easy', 'medium', 'hardcore'
var currentLanguage = storage.language || 'english'; // 'english' or 'spanish'
var requiredChildren = 4; // Changes based on difficulty
var maxWardrobes = 4; // Changes based on difficulty
// UI elements
var scoreText = new Text2('Score: 0', {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.visible = false;
LK.gui.topRight.addChild(scoreText);
var nightText = new Text2('Night: 1/99', {
size: 40,
fill: 0xFFFFFF
});
nightText.anchor.set(0.5, 0);
nightText.visible = false;
LK.gui.top.addChild(nightText);
var batteryText = new Text2('Battery: 100%', {
size: 30,
fill: 0xFFFF00
});
batteryText.anchor.set(0, 0);
batteryText.x = 20;
batteryText.y = 80;
batteryText.visible = false;
LK.gui.topLeft.addChild(batteryText);
var sageText = new Text2('Sage: 0', {
size: 30,
fill: 0x90EE90
});
sageText.anchor.set(0, 0);
sageText.x = 20;
sageText.y = 120;
sageText.visible = false;
LK.gui.topLeft.addChild(sageText);
var keyText = new Text2('Keys: 0', {
size: 30,
fill: 0xFFD700
});
keyText.anchor.set(0, 0);
keyText.x = 20;
keyText.y = 160;
keyText.visible = false;
LK.gui.topLeft.addChild(keyText);
var menuButton = new Text2('MENU', {
size: 30,
fill: 0xFFFFFF
});
menuButton.anchor.set(1, 0);
menuButton.x = -20;
menuButton.y = 20;
menuButton.visible = false;
LK.gui.topRight.addChild(menuButton);
var instructionText = new Text2('Tap to move. Triple tap near wardrobe to hide.', {
size: 24,
fill: 0xCCCCCC
});
instructionText.anchor.set(0.5, 1);
instructionText.visible = false;
LK.gui.bottom.addChild(instructionText);
// Create flashlight button
var flashlightButton = new Text2('š”', {
size: 60,
fill: 0xFFFF00
});
flashlightButton.anchor.set(1, 1);
flashlightButton.x = -20;
flashlightButton.y = -20;
flashlightButton.visible = false;
LK.gui.bottomRight.addChild(flashlightButton);
// Menu UI elements
var menuImage = LK.getAsset('menu', {
anchorX: 0.5,
anchorY: 0.5
});
menuImage.y = -150;
LK.gui.center.addChild(menuImage);
var titleText = new Text2('99 nights in the forest', {
size: 80,
fill: 0x90EE90
});
titleText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(titleText);
var playButton = new Text2('PLAY', {
size: 50,
fill: 0xFFFFFF
});
playButton.anchor.set(0.5, 0.5);
playButton.y = 100;
LK.gui.center.addChild(playButton);
var difficultyText = new Text2('EASY', {
size: 40,
fill: 0x90EE90
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.y = 50;
LK.gui.center.addChild(difficultyText);
var leftArrow = new Text2('<', {
size: 60,
fill: 0xFFFFFF
});
leftArrow.anchor.set(0.5, 0.5);
leftArrow.x = -120;
leftArrow.y = 50;
LK.gui.center.addChild(leftArrow);
var rightArrow = new Text2('>', {
size: 60,
fill: 0xFFFFFF
});
rightArrow.anchor.set(0.5, 0.5);
rightArrow.x = 120;
rightArrow.y = 50;
LK.gui.center.addChild(rightArrow);
var menuInstructions = new Text2('Rescue 4 children from the forest\nAvoid the stag or hide in wardrobes\nUse your flashlight to stun the stag', {
size: 30,
fill: 0xCCCCCC
});
menuInstructions.anchor.set(0.5, 0.5);
menuInstructions.y = 180;
LK.gui.center.addChild(menuInstructions);
var tutorialButton = new Text2('TUTORIAL', {
size: 40,
fill: 0x90EE90
});
tutorialButton.anchor.set(0.5, 0.5);
tutorialButton.y = 280;
LK.gui.center.addChild(tutorialButton);
var languageButton = new Text2('ENGLISH', {
size: 40,
fill: 0x90EE90
});
languageButton.anchor.set(0.5, 0.5);
languageButton.y = 320;
LK.gui.center.addChild(languageButton);
var tutorialText = new Text2('CONTROLS:\n⢠Tap anywhere to move\n⢠Triple tap near wardrobe to hide\n⢠Use flashlight button to stun stag\n⢠Collect keys from chests\n⢠Find and rescue children\n\nHOW TO PLAY:\n⢠Rescue all children to win\n⢠Avoid the stag or hide in wardrobes\n⢠Use flashlight wisely - limited battery\n⢠Collect sage for extra points\n⢠Game gets harder each night', {
size: 26,
fill: 0xFFFFFF
});
tutorialText.anchor.set(0.5, 0.5);
tutorialText.y = 0;
tutorialText.visible = false;
LK.gui.center.addChild(tutorialText);
var backButton = new Text2('BACK', {
size: 40,
fill: 0xFF6666
});
backButton.anchor.set(0.5, 0.5);
backButton.y = 250;
backButton.visible = false;
LK.gui.center.addChild(backButton);
// Flashlight button event handlers
flashlightButton.down = function (x, y, obj) {
if (player && player.batteryLevel > 0) {
player.toggleFlashlight();
}
};
// Difficulty selection event handlers
leftArrow.down = function (x, y, obj) {
if (difficulty === 'medium') {
difficulty = 'easy';
difficultyText.tint = 0x90EE90;
} else if (difficulty === 'hardcore') {
difficulty = 'medium';
difficultyText.tint = 0xFFAA00;
}
updateAllTexts();
};
rightArrow.down = function (x, y, obj) {
if (difficulty === 'easy') {
difficulty = 'medium';
difficultyText.tint = 0xFFAA00;
} else if (difficulty === 'medium') {
difficulty = 'hardcore';
difficultyText.tint = 0xFF0000;
}
updateAllTexts();
};
// Tutorial button event handler
tutorialButton.down = function (x, y, obj) {
// Hide menu elements
menuImage.visible = false;
titleText.visible = false;
playButton.visible = false;
tutorialButton.visible = false;
languageButton.visible = false;
menuInstructions.visible = false;
difficultyText.visible = false;
leftArrow.visible = false;
rightArrow.visible = false;
// Show tutorial elements
tutorialText.visible = true;
backButton.visible = true;
};
// Back button event handler
backButton.down = function (x, y, obj) {
// Hide tutorial elements
tutorialText.visible = false;
backButton.visible = false;
// Show menu elements
menuImage.visible = true;
titleText.visible = true;
playButton.visible = true;
tutorialButton.visible = true;
languageButton.visible = true;
menuInstructions.visible = true;
difficultyText.visible = true;
leftArrow.visible = true;
rightArrow.visible = true;
};
// Language button event handler
languageButton.down = function (x, y, obj) {
if (currentLanguage === 'english') {
currentLanguage = 'spanish';
} else {
currentLanguage = 'english';
}
storage.language = currentLanguage;
updateAllTexts();
};
// Function to update all text elements based on current language
function updateAllTexts() {
var texts = getTexts();
if (currentLanguage === 'spanish') {
// Update to Spanish
titleText.setText(texts.spanish.title);
playButton.setText(texts.spanish.play);
tutorialButton.setText(texts.spanish.tutorial);
languageButton.setText(texts.spanish.language);
backButton.setText(texts.spanish.back);
tutorialText.setText(texts.spanish.tutorialContent);
instructionText.setText(texts.spanish.instructions);
// Update difficulty text
if (difficulty === 'easy') {
difficultyText.setText(texts.spanish.easy);
menuInstructions.setText(texts.spanish.easyDesc);
} else if (difficulty === 'medium') {
difficultyText.setText(texts.spanish.medium);
menuInstructions.setText(texts.spanish.mediumDesc);
} else if (difficulty === 'hardcore') {
difficultyText.setText(texts.spanish.hardcore);
menuInstructions.setText(texts.spanish.hardcoreDesc);
}
} else {
// Update to English
titleText.setText(texts.english.title);
playButton.setText(texts.english.play);
tutorialButton.setText(texts.english.tutorial);
languageButton.setText(texts.english.language);
backButton.setText(texts.english.back);
tutorialText.setText(texts.english.tutorialContent);
instructionText.setText(texts.english.instructions);
// Update difficulty text
if (difficulty === 'easy') {
difficultyText.setText(texts.english.easy);
menuInstructions.setText(texts.english.easyDesc);
} else if (difficulty === 'medium') {
difficultyText.setText(texts.english.medium);
menuInstructions.setText(texts.english.mediumDesc);
} else if (difficulty === 'hardcore') {
difficultyText.setText(texts.english.hardcore);
menuInstructions.setText(texts.english.hardcoreDesc);
}
}
}
// Function that returns all text translations
function getTexts() {
return {
english: {
title: '99 nights in the forest',
play: 'PLAY',
tutorial: 'TUTORIAL',
language: 'ENGLISH',
back: 'BACK',
tutorialContent: 'CONTROLS:\n⢠Tap anywhere to move\n⢠Triple tap near wardrobe to hide\n⢠Use flashlight button to stun stag\n⢠Collect keys from chests\n⢠Find and rescue children\n\nHOW TO PLAY:\n⢠Rescue all children to win\n⢠Avoid the stag or hide in wardrobes\n⢠Use flashlight wisely - limited battery\n⢠Collect sage for extra points\n⢠Game gets harder each night',
instructions: 'Tap to move. Triple tap near wardrobe to hide.',
easy: 'EASY',
medium: 'MEDIUM',
hardcore: 'HARDCORE',
easyDesc: 'Rescue 4 children from the forest\nAvoid the stag or hide in wardrobes\nUse your flashlight to stun the stag',
mediumDesc: 'Rescue 6 children from the forest\nStag moves faster\nAvoid the stag or hide in wardrobes',
hardcoreDesc: 'Rescue 10 children from the forest\nOnly 2 wardrobes available\nStag moves very fast',
score: 'Score: ',
night: 'Night: ',
battery: 'Battery: ',
sage: 'Sage: ',
keys: 'Keys: ',
menu: 'MENU'
},
spanish: {
title: '99 noches en el bosque',
play: 'JUGAR',
tutorial: 'TUTORIAL',
language: 'ESPAĆOL',
back: 'VOLVER',
tutorialContent: 'CONTROLES:\n⢠Toca en cualquier lugar para moverte\n⢠Triple toque cerca del armario para esconderte\n⢠Usa el botón de linterna para aturdir al ciervo\n⢠Recoge llaves de los cofres\n⢠Encuentra y rescata a los niƱos\n\nCĆMO JUGAR:\n⢠Rescata a todos los niƱos para ganar\n⢠Evita al ciervo o escóndete en armarios\n⢠Usa la linterna sabiamente - baterĆa limitada\n⢠Recoge salvia para puntos extra\n⢠El juego se vuelve mĆ”s difĆcil cada noche',
instructions: 'Toca para moverte. Triple toque cerca del armario para esconderte.',
easy: 'FĆCIL',
medium: 'MEDIO',
hardcore: 'EXTREMO',
easyDesc: 'Rescata 4 niños del bosque\nEvita al ciervo o escóndete en armarios\nUsa tu linterna para aturdir al ciervo',
mediumDesc: 'Rescata 6 niños del bosque\nEl ciervo se mueve mÔs rÔpido\nEvita al ciervo o escóndete en armarios',
hardcoreDesc: 'Rescata 10 niƱos del bosque\nSolo 2 armarios disponibles\nEl ciervo se mueve muy rƔpido',
score: 'Puntuación: ',
night: 'Noche: ',
battery: 'BaterĆa: ',
sage: 'Salvia: ',
keys: 'Llaves: ',
menu: 'MENĆ'
}
};
}
// Play button event handler
playButton.down = function (x, y, obj) {
startGame();
};
// Menu button event handler
menuButton.down = function (x, y, obj) {
resetGame();
};
// Start game function
function startGame() {
gameState = 'playing';
// Set difficulty parameters
if (difficulty === 'easy') {
requiredChildren = 4;
maxWardrobes = 4;
} else if (difficulty === 'medium') {
requiredChildren = 6;
maxWardrobes = 4;
} else if (difficulty === 'hardcore') {
requiredChildren = 10;
maxWardrobes = 2;
}
// Hide menu UI
menuImage.visible = false;
titleText.visible = false;
playButton.visible = false;
tutorialButton.visible = false;
languageButton.visible = false;
menuInstructions.visible = false;
difficultyText.visible = false;
leftArrow.visible = false;
rightArrow.visible = false;
// Show game UI
scoreText.visible = true;
nightText.visible = true;
batteryText.visible = true;
sageText.visible = true;
keyText.visible = true;
instructionText.visible = true;
flashlightButton.visible = true;
menuButton.visible = true;
// Update menu button text
var texts = getTexts();
var currentTexts = currentLanguage === 'spanish' ? texts.spanish : texts.english;
menuButton.setText(currentTexts.menu);
// Initialize game elements
initializeGame();
// Start ambient music
LK.playMusic('forestAmbient');
}
// Initialize game elements
function initializeGame() {
// Create player
player = game.addChild(new Player());
player.x = 1024;
player.y = 2000;
// Create stag inside the cave
stag = game.addChild(new Stag());
stag.x = 1200; // Cave x position
stag.y = 1800; // Cave y position
// Create environments
var church = game.addChild(LK.getAsset('church', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 400
}));
environments.push(church);
var village = game.addChild(LK.getAsset('village', {
anchorX: 0.5,
anchorY: 0.5,
x: 1600,
y: 600
}));
environments.push(village);
var cave = game.addChild(LK.getAsset('cave', {
anchorX: 0.5,
anchorY: 0.5,
x: 1200,
y: 1800
}));
environments.push(cave);
// Create trees
for (var i = 0; i < 15; i++) {
var tree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: Math.random() * 2732
}));
environments.push(tree);
}
// Create children to rescue based on difficulty
var childNames = ['Luna', 'TomƔs', 'Ana', 'Leo', 'Sofia', 'Diego', 'Emma', 'Carlos', 'Mia', 'Alex'];
var childPositions = [{
x: 350,
y: 450
}, {
x: 1650,
y: 650
}, {
x: 1250,
y: 1850
}, {
x: 800,
y: 1200
}, {
x: 400,
y: 1600
}, {
x: 1700,
y: 1400
}, {
x: 600,
y: 800
}, {
x: 1400,
y: 1800
}, {
x: 900,
y: 500
}, {
x: 1100,
y: 2200
}];
for (var j = 0; j < requiredChildren; j++) {
var child = game.addChild(new Child(childNames[j]));
child.x = childPositions[j].x;
child.y = childPositions[j].y;
children.push(child);
}
// Create fog
for (var k = 0; k < 12; k++) {
var fog = game.addChild(new Fog());
fog.x = Math.random() * 2048;
fog.y = Math.random() * 2732;
fogElements.push(fog);
}
// Create sage items
for (var l = 0; l < 8; l++) {
var sage = game.addChild(new Sage());
sage.x = Math.random() * 1800 + 100;
sage.y = Math.random() * 2400 + 100;
sageElements.push(sage);
}
// Create chests - number of keys based on difficulty
var chestTypes = [];
var keyCount = requiredChildren; // Keys match required children count
var batteryCount = 10 - keyCount; // Remaining chests have batteries
// Add chests with keys based on difficulty
for (var k = 0; k < keyCount; k++) {
chestTypes.push(true);
}
// Add chests with batteries
for (var b = 0; b < batteryCount; b++) {
chestTypes.push(false);
}
// Shuffle the array to randomize positions
for (var shuffle = chestTypes.length - 1; shuffle > 0; shuffle--) {
var randomIndex = Math.floor(Math.random() * (shuffle + 1));
var temp = chestTypes[shuffle];
chestTypes[shuffle] = chestTypes[randomIndex];
chestTypes[randomIndex] = temp;
}
// Create the 10 chests with predetermined types
for (var m = 0; m < 10; m++) {
var containsKey = chestTypes[m];
var chest = game.addChild(new Chest(containsKey));
chest.x = Math.random() * 1800 + 100;
chest.y = Math.random() * 2400 + 100;
chestElements.push(chest);
}
// Create wardrobes for hiding based on difficulty
var wardrobePositions = [{
x: 500,
y: 800
}, {
x: 1500,
y: 1200
}, {
x: 800,
y: 2000
}, {
x: 1800,
y: 400
}];
for (var n = 0; n < maxWardrobes; n++) {
var wardrobe = game.addChild(new Wardrobe());
wardrobe.x = wardrobePositions[n].x;
wardrobe.y = wardrobePositions[n].y;
wardrobeElements.push(wardrobe);
}
}
// Movement variables
var targetX = null;
var targetY = null;
var pressStartTime = 0;
var isLongPress = false;
function checkWinCondition() {
if (rescuedChildren >= requiredChildren) {
LK.showYouWin();
}
}
function checkLoseCondition() {
// No lose condition - reaching 99 days is now winning
}
function resetGame() {
// Reset game variables
rescuedChildren = 0;
gameNight = 1;
isGameActive = true;
dayProgress = 0;
gameState = 'menu';
// Clear existing game objects
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
game.removeChild(child);
if (child.destroy) {
child.destroy();
}
}
// Clear arrays
children.length = 0;
fogElements.length = 0;
sageElements.length = 0;
keyElements.length = 0;
chestElements.length = 0;
wardrobeElements.length = 0;
environments.length = 0;
// Reset score
LK.setScore(0);
// Hide tutorial elements
tutorialText.visible = false;
backButton.visible = false;
// Show menu UI
menuImage.visible = true;
titleText.visible = true;
playButton.visible = true;
tutorialButton.visible = true;
languageButton.visible = true;
menuInstructions.visible = true;
difficultyText.visible = true;
leftArrow.visible = true;
rightArrow.visible = true;
// Initialize texts based on current language
updateAllTexts();
// Hide game UI
scoreText.visible = false;
nightText.visible = false;
batteryText.visible = false;
sageText.visible = false;
keyText.visible = false;
instructionText.visible = false;
flashlightButton.visible = false;
menuButton.visible = false;
// Stop music
LK.stopMusic();
}
// Event handlers
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
pressStartTime = LK.ticks;
isLongPress = false;
targetX = x;
targetY = y;
};
game.up = function (x, y, obj) {
if (gameState !== 'playing') return;
var currentTime = LK.ticks;
var pressDuration = currentTime - pressStartTime;
if (pressDuration <= 30 && !isLongPress) {
// Check for triple tap first
if (currentTime - lastTripleTapTime < 45) {
tripleTapCount++;
if (tripleTapCount === 3) {
// Triple tap detected - check for wardrobe interaction
var nearWardrobe = null;
for (var i = 0; i < wardrobeElements.length; i++) {
var wardrobe = wardrobeElements[i];
var distanceToWardrobe = Math.sqrt(Math.pow(player.x - wardrobe.x, 2) + Math.pow(player.y - wardrobe.y, 2));
if (distanceToWardrobe < wardrobe.detectionRadius) {
nearWardrobe = wardrobe;
break;
}
}
if (nearWardrobe) {
if (nearWardrobe.playerInside) {
nearWardrobe.exitWardrobe();
} else {
nearWardrobe.enterWardrobe();
}
}
tripleTapCount = 0;
return;
}
} else {
tripleTapCount = 1;
}
lastTripleTapTime = currentTime;
// Set movement target
targetX = x;
targetY = y;
}
};
game.move = function (x, y, obj) {
if (gameState !== 'playing') return;
if (!isLongPress && LK.ticks - pressStartTime < 30) {
targetX = x;
targetY = y;
}
};
// Main game update loop
game.update = function () {
if (gameState !== 'playing' || !isGameActive) return;
// Move player toward target
if (targetX !== null && targetY !== null && !player.isParalyzed) {
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
player.x += dx / distance * player.speed;
player.y += dy / distance * player.speed;
player.isMoving = true;
if (LK.ticks % 30 === 0) {
LK.getSound('footstep').play();
}
} else {
player.isMoving = false;
targetX = null;
targetY = null;
}
}
// Keep player in bounds
player.x = Math.max(50, Math.min(1998, player.x));
player.y = Math.max(50, Math.min(2682, player.y));
// Check child rescue interactions
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (!child.isRescued) {
var distanceToChild = Math.sqrt(Math.pow(player.x - child.x, 2) + Math.pow(player.y - child.y, 2));
if (distanceToChild < child.detectionRadius) {
child.rescue();
}
}
}
// Check sage collection
for (var j = 0; j < sageElements.length; j++) {
var sage = sageElements[j];
if (!sage.collected) {
var distanceToSage = Math.sqrt(Math.pow(player.x - sage.x, 2) + Math.pow(player.y - sage.y, 2));
if (distanceToSage < 40) {
sage.collect();
}
}
}
// Check key collection
for (var k = 0; k < keyElements.length; k++) {
var key = keyElements[k];
if (!key.collected) {
var distanceToKey = Math.sqrt(Math.pow(player.x - key.x, 2) + Math.pow(player.y - key.y, 2));
if (distanceToKey < 40) {
key.collect();
}
}
}
// Check chest collection
for (var l = 0; l < chestElements.length; l++) {
var chest = chestElements[l];
if (!chest.collected) {
var distanceToChest = Math.sqrt(Math.pow(player.x - chest.x, 2) + Math.pow(player.y - chest.y, 2));
if (distanceToChest < 40) {
chest.collect();
}
}
}
// Advance night every 30 seconds (1800 ticks) with acceleration based on rescued children
var dayAcceleration = rescuedChildren + 1; // 1x speed with 0 children, 2x with 1 child, etc.
// Add acceleration to day progress
dayProgress += dayAcceleration;
// Check if a full day has passed (1800 ticks worth of progress)
if (dayProgress >= 1800) {
gameNight++;
stag.speed += 0.1;
dayProgress -= 1800; // Reset progress but keep overflow
checkWinCondition(); // Check win condition instead of lose
}
// Check if player is inside a wardrobe and prevent movement
var playerInWardrobe = false;
for (var w = 0; w < wardrobeElements.length; w++) {
if (wardrobeElements[w].playerInside) {
playerInWardrobe = true;
targetX = null;
targetY = null;
break;
}
}
// Reset tap count if too much time has passed
if (LK.ticks - lastTapTime > 30) {
tapCount = 0;
}
// Reset triple tap count if too much time has passed
if (LK.ticks - lastTripleTapTime > 45) {
tripleTapCount = 0;
}
// Update UI
var texts = getTexts();
var currentTexts = currentLanguage === 'spanish' ? texts.spanish : texts.english;
scoreText.setText(currentTexts.score + LK.getScore());
nightText.setText(currentTexts.night + gameNight + '/99');
batteryText.setText(currentTexts.battery + Math.ceil(player.batteryLevel) + '%');
sageText.setText(currentTexts.sage + player.sageCount);
keyText.setText(currentTexts.keys + player.keyCount);
// Battery color warning
if (player.batteryLevel < 25) {
batteryText.tint = 0xFF0000;
} else if (player.batteryLevel < 50) {
batteryText.tint = 0xFFAA00;
} else {
batteryText.tint = 0xFFFF00;
}
// Update flashlight button appearance
if (player.flashlightOn) {
flashlightButton.tint = 0xFFFFFF; // Bright when on
flashlightButton.alpha = 1.0;
} else {
flashlightButton.tint = 0x888888; // Dim when off
flashlightButton.alpha = 0.6;
}
// Disable button when no battery
if (player.batteryLevel <= 0) {
flashlightButton.tint = 0x444444;
flashlightButton.alpha = 0.3;
}
};
// Game starts in menu state - no automatic initialization
roblox player. In-Game asset. 2d. High contrast. No shadows
Cabeza de Ciervo: La figura posee una cabeza detallada de ciervo, con astas ramificadas de color oscuro (posiblemente negro o marroĢn oscuro). Tiene orejas puntiagudas y un hocico prominente de un tono maĢs claro que el resto del cuerpo. Los ojos son grandes, completamente blancos con pupilas negras pequenĢas y redondas, lo que le confiere una mirada vaciĢa y sorprendente. La boca estaĢ abierta en una expresioĢn que podriĢa interpretarse como un grito o una mueca. Cuerpo Humanoide: El cuerpo es de forma vagamente humana, con dos brazos largos y delgados que terminan en manos con lo que parecen ser garras o dedos muy alargados. Las piernas son tambieĢn delgadas y la postura sugiere que estaĢ de pie. El color del cuerpo es principalmente marroĢn oscuro o morado oscuro.. In-Game asset. 2d. High contrast. No shadows
child roblox hat dinosaur red. In-Game asset. 2d. High contrast. No shadows
tree. In-Game asset. 2d. High contrast. No shadows
fog. In-Game asset. 2d. High contrast. No shadows
cave. In-Game asset. 2d. High contrast. No shadows
church gris. In-Game asset. 2d. High contrast. No shadows
sage. In-Game asset. 2d. High contrast. No shadows
roblox player corriendo de stag con miedo y una linterna en la mano. In-Game asset. 2d. High contrast. No shadows
cofre. In-Game asset. 2d. High contrast. No shadows
armario. In-Game asset. 2d. High contrast. No shadows
Cabeza de Ciervo: La figura posee una cabeza detallada de ciervo, con astas ramificadas de color oscuro (posiblemente negro o marroĢn oscuro). Tiene orejas puntiagudas y un hocico prominente de un tono maĢs claro que el resto del cuerpo. Los ojos son grandes, completamente blancos con pupilas rojas gigantes y redondas, lo que le confiere una mirada vaciĢa y sorprendente. La boca estaĢ abierta en una expresioĢn que podriĢa interpretarse como un grito o una mueca. Cuerpo Humanoide: El cuerpo es de forma vagamente humana, con dos brazos largos y delgados que terminan en manos con lo que parecen ser garras o dedos muy alargados. Las piernas son tambieĢn delgadas y la postura sugiere que estaĢ de pie. El color del cuerpo es principalmente marroĢn oscuro o morado oscuro.. In-Game asset. 2d. High contrast. No shadows
house. In-Game asset. 2d. High contrast. No shadows