import { Engine } from "./engine"; import { Player } from "./Player"; import { Settings } from "./Settings/Settings"; import { LiteratureNames } from "./Literature/data/LiteratureNames"; import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners"; import { createElement } from "../utils/uiHelpers/createElement"; import { createPopup } from "../utils/uiHelpers/createPopup"; import { removeElementById } from "../utils/uiHelpers/removeElementById"; // Ordered array of keys to Interactive Tutorial Steps const orderedITutorialSteps = [ "Start", "GoToCharacterPage", // Click on 'Stats' page "CharacterPage", // Introduction to 'Stats' page "CharacterGoToTerminalPage", // Go back to Terminal "TerminalIntro", // Introduction to Terminal "TerminalHelp", // Using 'help' Terminal command "TerminalLs", // Using 'ls' Terminal command "TerminalScan", // Using 'scan' Terminal command "TerminalScanAnalyze1", // Using 'scan-analyze' Terminal command "TerminalScanAnalyze2", // Using 'scan-analyze 3' Terminal command "TerminalConnect", // Connecting to n00dles "TerminalAnalyze", // Analyzing n00dles "TerminalNuke", // NUKE n00dles "TerminalManualHack", // Hack n00dles "TerminalHackingMechanics", // Explanation of hacking mechanics "TerminalCreateScript", // Create a script using 'nano' "TerminalTypeScript", // Script Editor page - Type script and then save & close "TerminalFree", // Using 'Free' Terminal command "TerminalRunScript", // Running script using 'run' Terminal command "TerminalGoToActiveScriptsPage", "ActiveScriptsPage", "ActiveScriptsToTerminal", "TerminalTailScript", "GoToHacknetNodesPage", "HacknetNodesIntroduction", "HacknetNodesGoToWorldPage", "WorldDescription", "TutorialPageInfo", "End", ]; // Create an 'enum' for the Steps const iTutorialSteps = {}; for (let i = 0; i < orderedITutorialSteps.length; ++i) { iTutorialSteps[orderedITutorialSteps[i]] = i; } const ITutorial = { currStep: 0, // iTutorialSteps.Start isRunning: false, // Keeps track of whether each step has been done stepIsDone: {}, }; function iTutorialStart() { // Initialize Interactive Tutorial state by settings 'done' for each state to false ITutorial.stepIsDone = {}; for (let i = 0; i < orderedITutorialSteps.length; ++i) { ITutorial.stepIsDone[i] = false; } Engine.loadTerminalContent(); // Don't autosave during this interactive tutorial Engine.Counters.autoSaveCounter = Infinity; ITutorial.currStep = 0; ITutorial.isRunning = true; document.getElementById("interactive-tutorial-container").style.display = "block"; // Exit tutorial button const exitButton = clearEventListeners("interactive-tutorial-exit"); exitButton.addEventListener("click", function () { iTutorialEnd(); return false; }); // Back button const backButton = clearEventListeners("interactive-tutorial-back"); backButton.addEventListener("click", function () { iTutorialPrevStep(); return false; }); // Next button const nextButton = clearEventListeners("interactive-tutorial-next"); nextButton.addEventListener("click", function () { iTutorialNextStep(); return false; }); iTutorialEvaluateStep(); } function iTutorialEvaluateStep() { if (!ITutorial.isRunning) { return; } // Disable and clear main menu // const terminalMainMenu = clearEventListeners("terminal-menu-link"); // const statsMainMenu = clearEventListeners("stats-menu-link"); // const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link"); // const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link"); // const cityMainMenu = clearEventListeners("city-menu-link"); // const tutorialMainMenu = clearEventListeners("tutorial-menu-link"); // terminalMainMenu.removeAttribute("class"); // statsMainMenu.removeAttribute("class"); // activeScriptsMainMenu.removeAttribute("class"); // hacknetMainMenu.removeAttribute("class"); // cityMainMenu.removeAttribute("class"); // tutorialMainMenu.removeAttribute("class"); // Interactive Tutorial Next button const nextBtn = document.getElementById("interactive-tutorial-next"); switch (ITutorial.currStep) { case iTutorialSteps.Start: Engine.loadTerminalContent(); iTutorialSetText( "Welcome to Bitburner, a cyberpunk-themed incremental RPG! " + "The game takes place in a dark, dystopian future... The year is 2077...

" + "This tutorial will show you the basics of the game. " + "You may skip the tutorial at any time.", ); nextBtn.style.display = "inline-block"; break; case iTutorialSteps.GoToCharacterPage: Engine.loadTerminalContent(); iTutorialSetText( "Let's start by heading to the Stats page. Click the Stats tab on " + "the main navigation menu (left-hand side of the screen)", ); nextBtn.style.display = "none"; break; case iTutorialSteps.CharacterPage: Engine.loadCharacterContent(); iTutorialSetText( "The Stats page shows a lot of important information about your progress, " + "such as your skills, money, and bonuses. ", ); nextBtn.style.display = "inline-block"; break; case iTutorialSteps.CharacterGoToTerminalPage: Engine.loadCharacterContent(); iTutorialSetText( "Let's head to your computer's terminal by clicking the Terminal tab on the " + "main navigation menu.", ); nextBtn.style.display = "none"; break; case iTutorialSteps.TerminalIntro: Engine.loadTerminalContent(); iTutorialSetText( "The Terminal is used to interface with your home computer as well as " + "all of the other machines around the world.", ); nextBtn.style.display = "inline-block"; break; case iTutorialSteps.TerminalHelp: Engine.loadTerminalContent(); iTutorialSetText( "Let's try it out. Start by entering the help command into the Terminal " + "(Don't forget to press Enter after typing the command)", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalLs: Engine.loadTerminalContent(); iTutorialSetText( "The help command displays a list of all available Terminal commands, how to use them, " + "and a description of what they do.

Let's try another command. Enter the ls command.", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalScan: Engine.loadTerminalContent(); iTutorialSetText( " ls is a basic command that shows files " + "on the computer. Right now, it shows that you have a program called NUKE.exe on your computer. " + "We'll get to what this does later.

Using your home computer's terminal, you can connect " + "to other machines throughout the world. Let's do that now by first entering " + "the scan command.", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalScanAnalyze1: Engine.loadTerminalContent(); iTutorialSetText( "The scan command shows all available network connections. In other words, " + "it displays a list of all servers that can be connected to from your " + "current machine. A server is identified by its hostname.

" + "That's great and all, but there's so many servers. Which one should you go to? " + "The scan-analyze command gives some more detailed information about servers on the " + "network. Try it now!", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalScanAnalyze2: Engine.loadTerminalContent(); iTutorialSetText( "You just ran scan-analyze with a depth of one. This command shows more detailed " + "information about each server that you can connect to (servers that are a distance of " + "one node away).

It is also possible to run scan-analyze with " + "a higher depth. Let's try a depth of two with the following command: scan-analyze 2.", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalConnect: Engine.loadTerminalContent(); iTutorialSetText( "Now you can see information about all servers that are up to two nodes away, as well " + "as figure out how to navigate to those servers through the network. You can only connect to " + "a server that is one node away. To connect to a machine, use the connect [hostname] command.

" + "From the results of the scan-analyze command, we can see that the n00dles server is " + "only one node away. Let's connect so it now using: connect n00dles", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalAnalyze: Engine.loadTerminalContent(); iTutorialSetText( "You are now connected to another machine! What can you do now? You can hack it!

In the year 2077, currency has " + "become digital and decentralized. People and corporations store their money " + "on servers and computers. Using your hacking abilities, you can hack servers " + "to steal money and gain experience.

" + "Before you try to hack a server, you should run diagnostics using the analyze command.", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalNuke: Engine.loadTerminalContent(); iTutorialSetText( "When the analyze command finishes running it will show useful information " + "about hacking the server.

For this server, the required hacking skill is only 1, " + "which means you can hack it right now. However, in order to hack a server " + "you must first gain root access. The NUKE.exe program that we saw earlier on your " + "home computer is a virus that will grant you root access to a machine if there are enough " + "open ports.

The analyze results shows that there do not need to be any open ports " + "on this machine for the NUKE virus to work, so go ahead and run the virus using the " + "run NUKE.exe command.", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalManualHack: Engine.loadTerminalContent(); iTutorialSetText( "You now have root access! You can hack the server using the hack command. " + "Try doing that now.", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalHackingMechanics: Engine.loadTerminalContent(); iTutorialSetText( "You are now attempting to hack the server. Performing a hack takes time and " + "only has a certain percentage chance " + "of success. This time and success chance is determined by a variety of factors, including " + "your hacking skill and the server's security level.

" + "If your attempt to hack the server is successful, you will steal a certain percentage " + "of the server's total money. This percentage is affected by your hacking skill and " + "the server's security level.

The amount of money on a server is not limitless. So, if " + "you constantly hack a server and deplete its money, then you will encounter " + "diminishing returns in your hacking.", ); nextBtn.style.display = "inline-block"; break; case iTutorialSteps.TerminalCreateScript: Engine.loadTerminalContent(); iTutorialSetText( "Hacking is the core mechanic of the game and is necessary for progressing. However, " + "you don't want to be hacking manually the entire time. You can automate your hacking " + "by writing scripts!

To create a new script or edit an existing one, you can use the nano " + "command. Scripts must end with the .script extension. Let's make a script now by " + "entering nano n00dles.script after the hack command finishes running (Sidenote: Pressing ctrl + c" + " will end a command like hack early)", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalTypeScript: Engine.loadScriptEditorContent("n00dles.script", ""); iTutorialSetText( "This is the script editor. You can use it to program your scripts. Scripts are " + "written in a simplified version of javascript. Copy and paste the following code into the script editor:

" + "
" +
          "while(true) {\n" +
          "  hack('n00dles');\n" +
          "}
" + "For anyone with basic programming experience, this code should be straightforward. " + "This script will continuously hack the n00dles server.

" + "To save and close the script editor, press the button in the bottom left, or press ctrl + b.", ); nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js) break; case iTutorialSteps.TerminalFree: Engine.loadTerminalContent(); iTutorialSetText( "Now we'll run the script. Scripts require a certain amount of RAM to run, and can be " + "run on any machine which you have root access to. Different servers have different " + "amounts of RAM. You can also purchase more RAM for your home server.

To check how much " + "RAM is available on this machine, enter the free command.", ); nextBtn.style.display = "none"; // next step triggered by terminal commmand break; case iTutorialSteps.TerminalRunScript: Engine.loadTerminalContent(); iTutorialSetText( "We have 4GB of free RAM on this machine, which is enough to run our " + "script. Let's run our script using run n00dles.script.", ); nextBtn.style.display = "none"; // next step triggered by terminal commmand break; case iTutorialSteps.TerminalGoToActiveScriptsPage: Engine.loadTerminalContent(); iTutorialSetText( "Your script is now running! " + "It will continuously run in the background and will automatically stop if " + "the code ever completes (the n00dles.script will never complete because it " + "runs an infinite loop).

These scripts can passively earn you income and hacking experience. " + "Your scripts will also earn money and experience while you are offline, although at a " + "slightly slower rate.

" + "Let's check out some statistics for our running scripts by clicking the " + "Active Scripts link in the main navigation menu.", ); nextBtn.style.display = "none"; break; case iTutorialSteps.ActiveScriptsPage: Engine.loadActiveScriptsContent(); iTutorialSetText( "This page displays information about all of your scripts that are " + "running across every server. You can use this to gauge how well " + "your scripts are doing. Let's go back to the Terminal", ); nextBtn.style.display = "none"; break; case iTutorialSteps.ActiveScriptsToTerminal: Engine.loadTerminalContent(); iTutorialSetText( "One last thing about scripts, each active script contains logs that detail " + "what it's doing. We can check these logs using the tail command. Do that " + "now for the script we just ran by typing tail n00dles.script", ); nextBtn.style.display = "none"; // next step triggered by terminal command break; case iTutorialSteps.TerminalTailScript: Engine.loadTerminalContent(); iTutorialSetText( "The log for this script won't show much right now (it might show nothing at all) because it " + "just started running...but check back again in a few minutes!

" + "This covers the basics of hacking. To learn more about writing " + "scripts, select the Tutorial link in the " + "main navigation menu to look at the documentation. " + "If you are an experienced JavaScript " + "developer, I would highly suggest you check out the section on " + "NetscriptJS/Netscript 2.0, it's faster and more powerful.

For now, let's move on to something else!", ); nextBtn.style.display = "inline-block"; break; case iTutorialSteps.GoToHacknetNodesPage: Engine.loadTerminalContent(); iTutorialSetText( "Hacking is not the only way to earn money. One other way to passively " + "earn money is by purchasing and upgrading Hacknet Nodes. Let's go to " + "the Hacknet page through the main navigation menu now.", ); nextBtn.style.display = "none"; break; case iTutorialSteps.HacknetNodesIntroduction: Engine.loadHacknetNodesContent(); iTutorialSetText( "here you can purchase new Hacknet Nodes and upgrade your " + "existing ones. Let's purchase a new one now.", ); nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js) break; case iTutorialSteps.HacknetNodesGoToWorldPage: Engine.loadHacknetNodesContent(); iTutorialSetText( "You just purchased a Hacknet Node! This Hacknet Node will passively " + "earn you money over time, both online and offline. When you get enough " + " money, you can upgrade " + "your newly-purchased Hacknet Node below.

" + "Let's go to the City page through the main navigation menu.", ); nextBtn.style.display = "none"; break; case iTutorialSteps.WorldDescription: Engine.loadLocationContent(); iTutorialSetText( "This page lists all of the different locations you can currently " + "travel to. Each location has something that you can do. " + "There's a lot of content out in the world, make sure " + "you explore and discover!

" + "Lastly, click on the Tutorial link in the main navigation menu.", ); nextBtn.style.display = "none"; break; case iTutorialSteps.TutorialPageInfo: Engine.loadTutorialContent(); iTutorialSetText( "This page contains a lot of different documentation about the game's " + "content and mechanics. I know it's a lot, but I highly suggest you read " + "(or at least skim) through this before you start playing. That's the end of the tutorial. " + "Hope you enjoy the game!", ); nextBtn.style.display = "inline-block"; nextBtn.innerHTML = "Finish Tutorial"; break; case iTutorialSteps.End: iTutorialEnd(); break; default: throw new Error("Invalid tutorial step"); } if (ITutorial.stepIsDone[ITutorial.currStep] === true) { nextBtn.style.display = "inline-block"; } } // Go to the next step and evaluate it function iTutorialNextStep() { ITutorial.stepIsDone[ITutorial.currStep] = true; if (ITutorial.currStep < iTutorialSteps.End) { ITutorial.currStep += 1; } iTutorialEvaluateStep(); } // Go to previous step and evaluate function iTutorialPrevStep() { if (ITutorial.currStep > iTutorialSteps.Start) { ITutorial.currStep -= 1; } iTutorialEvaluateStep(); } function iTutorialEnd() { // Re-enable auto save if (Settings.AutosaveInterval === 0) { Engine.Counters.autoSaveCounter = Infinity; } else { Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5; } Engine.init(); ITutorial.currStep = iTutorialSteps.End; ITutorial.isRunning = false; document.getElementById("interactive-tutorial-container").style.display = "none"; // Create a popup with final introductory stuff const popupId = "interactive-tutorial-ending-popup"; const txt = createElement("p", { innerHTML: "If you are new to the game, the following links may be useful for you!

" + "Getting Started Guide" + "Documentation

" + "The Beginner's Guide to Hacking was added to your home computer! It contains some tips/pointers for starting out with the game. " + "To read it, go to Terminal and enter

cat " + LiteratureNames.HackersStartingHandbook, }); const gotitBtn = createElement("a", { class: "a-link-button", float: "right", padding: "6px", innerText: "Got it!", clickListener: () => { removeElementById(popupId); }, }); createPopup(popupId, [txt, gotitBtn]); Player.getHomeComputer().messages.push(LiteratureNames.HackersStartingHandbook); } let textBox = null; (function () { function set() { textBox = document.getElementById("interactive-tutorial-text"); document.removeEventListener("DOMContentLoaded", set); } document.addEventListener("DOMContentLoaded", set); })(); function iTutorialSetText(txt) { textBox.innerHTML = txt; textBox.parentElement.scrollTop = 0; // this resets scroll position } export { iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, ITutorial };