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.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). 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! 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.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.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!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!