JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
<div id="start-screen"> <button id="start-button" onclick="ambientHum.play?.(); SugarCube.Engine.play('NewRecruits');"> BEGIN </button> <style> /* Full-screen CRT-style background */ #start-screen { display: flex; justify-content: center; align-items: center; height: 100vh; background: repeating-linear-gradient( 0deg, #000, #000 2px, #010101 3px, #000 4px ); background-image: linear-gradient(transparent 50%, rgba(0, 255, 0, 0.03) 50%); background-size: 100% 4px; } /* Custom START button */ #start-button { font-family: 'Glass TTY VT220', monospace; font-size: 1.1rem; color: black; background-color: #00ff00; border: none; padding: 0.4em 1.5em; border-radius: 0.25em; cursor: pointer; text-transform: uppercase; text-shadow: 0 0 6px #00ff00; box-shadow: 0 0 10px #00ff00; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; line-height: 1.2; animation: flicker 2.5s infinite; } #start-button:hover { background-color: #00cc00; box-shadow: 0 0 15px #00ff00; } @keyframes flicker { 0% { opacity: 1; transform: none; } 3% { opacity: 0.3; transform: translate(-1px, 1px) scale(1.01); } 5% { opacity: 0.8; transform: translate(1px, -2px) scale(0.98); } 8% { opacity: 0.2; transform: translate(-1px, 1px) scale(1.02); } 10% { opacity: 1; transform: none; } 13% { opacity: 0.5; transform: translate(2px, 0) scale(1.03); } 16% { opacity: 1; transform: none; } 23% { opacity: 0.6; transform: translate(0, 2px) scale(0.97); } 26% { opacity: 0.3; transform: translate(-2px, -1px) scale(1.02); } 30% { opacity: 1; transform: none; } 35% { opacity: 0.4; transform: translate(1px, 1px) scale(1.01); } 100% { opacity: 1; transform: none; } } </style> </div>
<<nobr>> <script> new Audio("file:///Users/jimlong/Documents/Twine/Stories/sounds/beep.mp3").play().catch(function(e) { console.log("Beep playback blocked:", e); }); </script> <script> window.classMusic = new Audio("file:///Users/jimlong/Documents/Twine/Stories/sounds/class.mp3"); window.classMusic.loop = false; window.classMusic.volume = 0.2; window.classMusic.play().catch(function(e) { console.log("Music playback blocked:", e); }); </script> <div class="NewRecruits"> <h1><blink>CASE OFFICER RECRUIT TRAINING MODULE</blink></h1> <div id="typewriter-container"> <p id="para1" class="typewriter-text" style="display: none;"></p> <p id="para2" class="typewriter-text" style="display: none;"></p> <p id="para3" class="typewriter-text" style="display: none;"></p> <p id="para4" class="typewriter-text" style="display: none;"></p> <p id="para5" class="typewriter-text" style="display: none;"></p> </div> <script> setTimeout(() => { (function() { const paragraphs = [ "Welcome to THE FARM. The agency uses games to train analysts and case officers about complex adaptive systems, targeting, recruiting and tradecraft.", " Tabletop, seminar and VR games are used to expose recruits to the types of challenges they will face in the field. This game is a hybrid approach and you are in the inaugural class.", "In this scenario you will take everything you learned in those exercises and apply it to real world operations. Working assets, surveillance, tradecraft…PLUS how all of that impacts your life outside of work.", "Here's your legend: You're tasked to the Domestic Intelligence Assessment Unit - DIAU. It's a lean, off-books, agile, hybrid team of analysts and officers. It's been hastily stood up in a time of political rot as a backstop against institutional decay in the Intelligence Community.", "Your team is a mixed bag in terms of experience and skill. You will not be working out of Langley. DIAU operations has been relegated to the old Charles Ruth Building in Bethesda, Maryland. Please click START COURSE below:" ]; let currentParagraph = 0; const typeSpeed = 35; const pauseBetweenParagraphs = 750; function typeWriter(element, text, callback) { if (!element) { console.error("Element not found!"); return; } element.style.display = "block"; element.innerHTML = ""; let i = 0; function type() { if (i < text.length) { element.innerHTML += text.charAt(i); i++; setTimeout(type, typeSpeed); } else if (callback) { setTimeout(callback, pauseBetweenParagraphs); } } type(); } function startNextParagraph() { if (currentParagraph < paragraphs.length) { const element = document.getElementById("para" + (currentParagraph + 1)); typeWriter(element, paragraphs[currentParagraph], () => { currentParagraph++; startNextParagraph(); }); } else { const lastParagraph = document.getElementById("para" + paragraphs.length); if (lastParagraph) { lastParagraph.classList.add("typewriter-cursor"); } setTimeout(() => { const startBtn = document.getElementById("start-button"); if (startBtn) { startBtn.style.display = "block"; } }, pauseBetweenParagraphs); } } startNextParagraph(); })(); }, 100); </script> <div id="start-button" style="display: none;"> <a href="javascript:void(0)" onclick="if (window.classMusic) { window.classMusic.pause(); window.classMusic.currentTime = 0; } SugarCube.Engine.play('Classroom');">START COURSE</a> </div> </div> <</nobr>>
This is the Dossiers passage. [[Continue -> Classroom]]
<html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> </style> </head> <div class="slideshow-container"> <!-- Full-width images with number and caption text --> <div class="mySlides fade"> <div class="numbertext">1 / 3</div> <img src="file:///Users/jimlong/Documents/Twine/Stories/images/class_1.png" style="width:551px;height:887px;"> <div class="text">Caption Text</div> </div> <div class="mySlides fade"> <div class="numbertext">2 / 3</div> <img src="file:///Users/jimlong/Documents/Twine/Stories/images/Class_2.png" style="width:551px;height:887px;"> <div class="text">Caption Two</div> </div> <div class="mySlides fade"> <div class="numbertext">3 / 3</div> <img src="file:///Users/jimlong/Documents/Twine/Stories/images/Class_3.png" style="width:551px;height:887px;> <div class="text">Caption Three</div> </div> <!-- Next and previous buttons --> <a class="prev" onclick="plusSlides(-1)">❮</a> <a class="next" onclick="plusSlides(1)">❯</a> </div> <br> <!-- The dots/circles --> <div style="text-align:center"> <span class="dot" onclick="currentSlide(1)"></span> <span class="dot" onclick="currentSlide(2)"></span> <span class="dot" onclick="currentSlide(3)"></span> </div> </html>
<div class="slideshow-container"> <div class="mySlides fade"> <img src="file:///Users/jimlong/Documents/Twine/Stories/images/class_1.png"> </div> <div class="mySlides fade"> <img src="file:///Users/jimlong/Documents/Twine/Stories/images/Class_2.png"> </div> <div class="mySlides fade"> <img src="file:///Users/jimlong/Documents/Twine/Stories/images/Class_3.png"> </div> <a class="prev" onclick="plusSlides(-1)">❮</a> <a class="next" onclick="plusSlides(1)">❯</a> </div>
<<nobr>> <div class="video-wrapper"> <div class="overlay-text" id="overlay-text-classroom">CLICK PLAY TO BEGIN TRAINING</div> <video id="classroom-video" playsinline controls style="width: 551px; height: 887px;"> <source src="file:///Users/jimlong/Documents/Twine/Stories/video/class.MP4" type="video/mp4"> Your browser does not support the video tag. </video> </div> <div id="continue-button" style="display: none;"> <a href="javascript:void(0)" onclick="SugarCube.Engine.play('Dossiers');"> DOSSIERS → </a> </div> <</nobr>> <<script>> setTimeout(function() { var video = document.getElementById('classroom-video'); var overlayText = document.getElementById('overlay-text-classroom'); var videoWrapper = document.querySelector('.video-wrapper'); var continueButton = document.getElementById('continue-button'); if (video && overlayText && videoWrapper && continueButton) { video.onplay = function() { overlayText.style.display = 'none'; }; video.onended = function() { videoWrapper.style.display = 'none'; continueButton.style.display = 'block'; }; } }, 200); <</script>>
<div id="start-screen"> <button id="start-button" onclick="ambientHum.play?.(); SugarCube.Engine.play('Login');"> <span id="scramble-text">œß¥ŽΣπ</span> </button> <script> (function() { const scrambleEl = document.getElementById('scramble-text'); const finalText = 'START'; const characters = 'œß¥ŽΣπ∆©ƒ∂åΩ≈ç√∫˜µ≤≥÷¡™£¢∞§¶•ªº'; let frame = 0; const totalFrames = 20; // Number of scramble iterations function scramble() { if (frame < totalFrames) { let scrambled = ''; for (let i = 0; i < finalText.length; i++) { // Gradually reveal the correct letter if (frame > (i * totalFrames / finalText.length)) { scrambled += finalText[i]; } else { scrambled += characters[Math.floor(Math.random() * characters.length)]; } } scrambleEl.textContent = scrambled; frame++; setTimeout(scramble, 50); // Speed of scrambling (50ms per frame) } else { scrambleEl.textContent = finalText; } } // Start scrambling after a short delay setTimeout(scramble, 500); })(); </script> <style> /* Full-screen CRT-style background */ #start-screen { display: flex; justify-content: center; align-items: center; height: 100vh; background: repeating-linear-gradient( 0deg, #000, #000 2px, #010101 3px, #000 4px ); background-image: linear-gradient(transparent 50%, rgba(0, 255, 0, 0.03) 50%); background-size: 100% 4px; } /* Custom START button */ #start-button { font-family: 'Glass TTY VT220', monospace; font-size: 1.1rem; color: black; background-color: #00ff00; border: none; padding: 0.4em 1.5em; border-radius: 0.25em; cursor: pointer; text-transform: uppercase; text-shadow: 0 0 2px #00ff00; box-shadow: 0 0 10px #00ff00; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; line-height: 1.2; animation: flicker 2.5s infinite; } #start-button:hover { background-color: #00cc00; box-shadow: 0 0 15px #00ff00; } @keyframes flicker { 0% { opacity: 1; transform: none; } 3% { opacity: 0.3; transform: translate(-1px, 1px) scale(1.01); } 5% { opacity: 0.8; transform: translate(1px, -2px) scale(0.98); } 8% { opacity: 0.2; transform: translate(-1px, 1px) scale(1.02); } 10% { opacity: 1; transform: none; } 13% { opacity: 0.5; transform: translate(2px, 0) scale(1.03); } 16% { opacity: 1; transform: none; } 23% { opacity: 0.6; transform: translate(0, 2px) scale(0.97); } 26% { opacity: 0.3; transform: translate(-2px, -1px) scale(1.02); } 30% { opacity: 1; transform: none; } 35% { opacity: 0.4; transform: translate(1px, 1px) scale(1.01); } 100% { opacity: 1; transform: none; } } </style> </div>
<!-- Login Passage --> <<script>> new Audio("/sounds/Old computer beep.mp3").play().catch(function(e) { console.log("Beep playback blocked:", e); }); <</script>> <<set _loginAttempts = $loginAttempts>> <<set _showError = $showError>> <<set _accessGranted = $accessGranted>> <div class="login-terminal"> <nobr> <p>CLASSIFIED SYSTEM - AUTHORIZED PERSONNEL ONLY<span class="blink-cursor"></span></p> <hr style="border-color: #00ff00;"> <h3> █ SECURE ACCESS TERMINAL █</h3> </nobr> <<if _loginAttempts gte 3>> <p class="error-message">⚠️ MAXIMUM ATTEMPTS EXCEEDED</p> <p class="error-message">SYSTEM LOCKED FOR SECURITY</p> <p><em><<link "Help" "LoginHelp">><</link>></em></p> <<else>> <<if _showError and _loginAttempts gt 0>> <p class="error-message">❌ ACCESS DENIED - INVALID CREDENTIALS ❌</p> <p class="error-message">ATTEMPTS REMAINING: <<= 3 - $loginAttempts>></p> <</if>> <div class="login-form"> <h3>ENTER PLAINTEXT THEN <span class="blink"><u>CLICK</u></span> THE "AUTHENTICATE" BUTTON:</h3> <label>USERNAME:</label> <<textbox "$inputUsername" "" autofocus>> <label>PASSWORD:</label> <<textbox "$inputPassword" "" "password">> <<button "AUTHENTICATE">> <<run _username = $inputUsername.toLowerCase()>> <<if _username is "kumother" and $inputPassword is "Angleton">> <<set $accessGranted = true>> <<set $playerCodename = $inputUsername.toUpperCase()>> <<run accessGranted.pause(); accessGranted.currentTime = 0; accessGranted.play();>> <<goto "MissionBriefing">> <<else>> <<set $loginAttempts += 1>> <<set $showError = true>> <<if $loginAttempts gte 3>> <<run maxAttemptsSound.pause(); maxAttemptsSound.currentTime = 0; maxAttemptsSound.play();>> <<else>> <<run alertSound.pause(); alertSound.currentTime = 0; alertSound.play();>> <</if>> <<goto "Login">> <</if>> <</button>> </div> <</if>> </div> <script> $(document).on('keydown', function (e) { if (e.key === "Enter") { // Only intercept if focused element is one of the SugarCube textboxes const el = document.activeElement; if (el && (el.type === "text" || el.type === "password")) { e.preventDefault(); // STOP SugarCube from navigating e.stopImmediatePropagation(); // Click the AUTHENTICATE button $("tw-button:contains('AUTHENTICATE')").click(); return false; } } }); </script>
/* Initialize variables if not set / <<if not def $loginAttempts>><<set $loginAttempts to 0>><</if>> <<if not def $showError>><<set $showError to false>><</if>> <<if not def $accessGranted>><<set $accessGranted to false>><</if>> / Second Passage: "MissionBriefing" */ <div class="login-terminal"> <p class="access-granted">✓ ACCESS GRANTED</p> <p class="access-granted">WELCOME TO AGENCEY RECRUIT ONBOARDING</p> <hr style="border-color: #00ff00;"> <h3>BEFORE YOU BEGIN YOUR TRAINING YOU MUST SUBMIT TO A POLYGRAPH - CLICK "NEXT" TO PROCEED<span class="blink-cursor"></span></h3> <<link "NEXT" "Polygraph">><</link>> </div>
This is the Polygraph passage You passed! Welcome New Recruit! [[NewRecruits]]
<<script>> window.onbeforeunload = function () { // Force reload to go back to the beginning window.location.href = window.location.origin + window.location.pathname; }; <</script>> <<set $loginAttempts to 0>> <<set $showError to false>> <<set $accessGranted to false>>
<<nobr>> <script> new Audio("file:///Users/jimlong/Documents/Twine/Stories/sounds/beep.mp3").play().catch(function(e) { console.log("Beep playback blocked:", e); }); </script> <script> window.classMusic = new Audio("file:///Users/jimlong/Documents/Twine/Stories/sounds/class.mp3"); window.classMusic.loop = false; window.classMusic.volume = 0.2; window.classMusic.play().catch(function(e) { console.log("Music playback blocked:", e); }); </script> <div class="NewRecruits"> <h1>YOU NEED HELP LOGGING IN</h1> <div id="typewriter-container"> <p id="para1" class="typewriter-text" style="display: none;"></p> <p id="para2" class="typewriter-text" style="display: none;"></p> <p id="para3" class="typewriter-text" style="display: none;"></p> <p id="para4" class="typewriter-text" style="display: none;"></p> <p id="para5" class="typewriter-text" style="display: none;"></p> </div> <script> setTimeout(() => { (function() { const paragraphs = [ {text: `This is a test - a test to see if you understand basic intelligence gathering disciplines and tradecraft.`, html: null}, {text: `First we will test your OSINT skills. The login and password are out in the open HERE`, html: `First we will test your OSINT skills. The login and password are out in the open <a href="https://www.cia.gov/resources/csi/static/Cunning-Passages-Contrived-Corridors.pdf" class="error-message" target="_blank">HERE</a>`}, {text: `Now SIGINT. This login and password are stored within this image. Use steganography tools to extract them.`, html: null}, {text: `Next is HUMINT. You will need to find someone who knows the credentials and convince them to share.`, html: null}, {text: `Finally, TECHINT. Analyze the network traffic to intercept the authentication token.`, html: null} ]; let currentParagraph = 0; const typeSpeed = 35; const pauseBetweenParagraphs = 750; function typeWriter(element, paragraphObj, callback) { if (!element) { console.error("Element not found!"); return; } element.style.display = "block"; element.textContent = ""; let i = 0; const textToType = paragraphObj.text; function type() { if (i < textToType.length) { element.textContent += textToType.charAt(i); i++; setTimeout(type, typeSpeed); } else { if (paragraphObj.html) { element.innerHTML = paragraphObj.html; } if (callback) { setTimeout(callback, pauseBetweenParagraphs); } } } type(); } function startNextParagraph() { if (currentParagraph < paragraphs.length) { const element = document.getElementById("para" + (currentParagraph + 1)); typeWriter(element, paragraphs[currentParagraph], () => { currentParagraph++; startNextParagraph(); }); } else { const lastParagraph = document.getElementById("para" + paragraphs.length); if (lastParagraph) { lastParagraph.classList.add("typewriter-cursor"); } setTimeout(() => { const startBtn = document.getElementById("start-button"); if (startBtn) { startBtn.style.display = "block"; } }, pauseBetweenParagraphs); } } startNextParagraph(); })(); }, 100); </script> <div id="start-button" style="display: none;"> <a href="javascript:void(0)" onclick="if (window.classMusic) { window.classMusic.pause(); window.classMusic.currentTime = 0; } SugarCube.Engine.play('Login');">TRY AGAIN</a> </div> </div> <</nobr>>
<<nobr>> <script> new Audio("file:///Users/jimlong/Documents/Twine/Stories/sounds/beep.mp3").play().catch(function(e) { console.log("Beep playback blocked:", e); }); </script> <script> window.classMusic = new Audio("file:///Users/jimlong/Documents/Twine/Stories/sounds/class.mp3"); window.classMusic.loop = false; window.classMusic.volume = 0.2; window.classMusic.play().catch(function(e) { console.log("Music playback blocked:", e); }); </script> <div class="NewRecruits"> <h1>YOU NEED HELP LOGGING IN</h1> <div id="typewriter-container"> <p id="para1" class="typewriter-text" style="display: none;"></p> <p id="para2" class="typewriter-text" style="display: none;"></p> <p id="para3" class="typewriter-text" style="display: none;"></p> <p id="para4" class="typewriter-text" style="display: none;"></p> <p id="para5" class="typewriter-text" style="display: none;"></p> </div> <script> setTimeout(() => { (function() { const paragraphs = [ {text: `If you understand basic intelligence gathering disciplines and tradecraft, you'll be able to figure out the login and password. You will only need to crack one of the following:`, html: null}, {text: `First we will test your OSINT skills. The login and password are out in the open HERE`, html: `First we will test your OSINT skills. The login and password are out in the open <a href="https://www.cia.gov/resources/csi/static/Cunning-Passages-Contrived-Corridors.pdf" class="error-message" target="_blank">HERE</a>`}, {text: `IMAGE ANALYSIS: The login and password are stored within this image. Download the image and extract the login credentials using the open source online steganography tool found HERE.`, html: `IMAGE ANALYSIS: The login and password are stored within this image. Download the image and extract the login credentials using the open source online steganography tool found <a href="https://futureboy.us/stegano/decinput.html" class="error-message" target="_blank">HERE</a .<br><br><center><a href="file:///Users/jimlong/Documents/Twine/Stories/Images/ONIONsteg.jpg" download="ONIONsteg.jpg"><img src="file:///Users/jimlong/Documents/Twine/Stories/Images/ONIONsteg.jpg" style="max-width: 50%; border: 1px solid #00ff00; cursor: pointer; margin-top: 10px; background: transparent;" alt="Steganography Image"></a></center>`}, {text: `Next is HUMINT. You will need to find someone who knows the credentials and convince them to share.`, html: null}, {text: `Finally, TECHINT. Analyze the network traffic to intercept the authentication token.`, html: null} ]; let currentParagraph = 0; const typeSpeed = 35; const pauseBetweenParagraphs = 750; function typeWriter(element, paragraphObj, callback) { if (!element) { console.error("Element not found!"); return; } element.style.display = "block"; element.textContent = ""; let i = 0; const textToType = paragraphObj.text; function type() { if (i < textToType.length) { element.textContent += textToType.charAt(i); i++; setTimeout(type, typeSpeed); } else { if (paragraphObj.html) { element.innerHTML = paragraphObj.html; } if (callback) { setTimeout(callback, pauseBetweenParagraphs); } } } type(); } function startNextParagraph() { if (currentParagraph < paragraphs.length) { const element = document.getElementById("para" + (currentParagraph + 1)); typeWriter(element, paragraphs[currentParagraph], () => { currentParagraph++; startNextParagraph(); }); } else { const lastParagraph = document.getElementById("para" + paragraphs.length); if (lastParagraph) { lastParagraph.classList.add("typewriter-cursor"); } setTimeout(() => { const startBtn = document.getElementById("start-button"); if (startBtn) { startBtn.style.display = "block"; } }, pauseBetweenParagraphs); } } startNextParagraph(); })(); }, 100); </script> <div id="start-button" style="display: none;"> <a href="javascript:void(0)" onclick="if (window.classMusic) { window.classMusic.pause(); window.classMusic.currentTime = 0; } SugarCube.Engine.play('Login');">TRY AGAIN</a> </div> </div> <</nobr>>