diff --git a/css/interactivetutorial.scss b/css/interactivetutorial.scss
index 15b8f3cc9..0c5aa3fdd 100644
--- a/css/interactivetutorial.scss
+++ b/css/interactivetutorial.scss
@@ -11,11 +11,11 @@
position: absolute; /* Stay in place */
right: 0;
top: 0;
- height: 400px; /* Full height */
+ height: 450px;
padding: 10px;
border: 5px solid #fff;
- width: 20%;
- overflow: auto; /* Enable scroll if needed */
+ width: 23%;
+ overflow:hidden;
background-color: #444; /* Fallback color */
color: #fff;
@@ -29,6 +29,9 @@
margin: 4px;
color: #fff;
background-color: #444;
+ font-size: $defaultFontSize * 0.875;
+ max-height: 350px;
+ overflow-y: auto;
}
#interactive-tutorial-exit,
@@ -38,7 +41,7 @@
@include boxShadow(1px 1px 3px #000);
color: #aaa;
- font-size: $defaultFontSize * 1.25;
+ font-size: $defaultFontSize * 1.125;
font-weight: bold;
background-color: #000;
@@ -50,6 +53,7 @@
}
}
+/*
#interactive-tutorial-exit {
float: left;
}
@@ -58,6 +62,16 @@
margin-right: 20%;
float: right;
}
+*/
+#interactive-tutorial-exit {
+ position:absolute;
+ bottom: 0;
+ left:0;
+}
+
+#interactive-tutorial-back {
+ float: left;
+}
#interactive-tutorial-next {
float: right;
diff --git a/src/HacknetNode.js b/src/HacknetNode.js
index 60a956ce9..edb212c05 100644
--- a/src/HacknetNode.js
+++ b/src/HacknetNode.js
@@ -2,7 +2,7 @@ import {BitNodeMultipliers} from "./BitNodeMultipliers";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {iTutorialSteps, iTutorialNextStep,
- iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
+ ITutorial} from "./InteractiveTutorial";
import {Player} from "./Player";
import {dialogBoxCreate} from "../utils/DialogBox";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
@@ -245,8 +245,8 @@ Reviver.constructors.HacknetNode = HacknetNode;
function purchaseHacknet() {
/* INTERACTIVE TUTORIAL */
- if (iTutorialIsRunning) {
- if (currITutorialStep == iTutorialSteps.HacknetNodesIntroduction) {
+ if (ITutorial.isRunning) {
+ if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
iTutorialNextStep();
} else {
return;
diff --git a/src/InteractiveTutorial.js b/src/InteractiveTutorial.js
index 953b52cb5..5e3affe09 100644
--- a/src/InteractiveTutorial.js
+++ b/src/InteractiveTutorial.js
@@ -1,55 +1,77 @@
import {Engine} from "./engine";
import {Player} from "./Player";
-import {dialogBoxCreate} from "../utils/DialogBox";
+import {Settings} from "./Settings";
+import {Terminal} from "./Terminal";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
+import {createElement} from "../utils/uiHelpers/createElement";
+import {createPopup} from "../utils/uiHelpers/createPopup";
+import {removeElementById} from "../utils/uiHelpers/removeElementById";
-/* InteractiveTutorial.js */
-let iTutorialSteps = {
- Start: "Start",
- GoToCharacterPage: "Click on the Character page menu link",
- CharacterPage: "Introduction to Character page",
- CharacterGoToTerminalPage: "Click on the Terminal link",
- TerminalIntro: "Introduction to terminal interface",
- TerminalHelp: "Using the help command to display all options in terminal",
- TerminalLs: "Use the ls command to show all programs/scripts. Right now we have NUKE.exe",
- TerminalScan: "Using the scan command to display all available connections",
- TerminalScanAnalyze1: "Use the scan-analyze command to show hacking related information",
- TerminalScanAnalyze2: "Use the scan-analyze command with a depth of 3",
- TerminalConnect: "Using the telnet/connect command to connect to another server",
- TerminalAnalyze: "Use the analyze command to display details about this server",
- TerminalNuke: "Use the NUKE Program to gain root access to a server",
- TerminalManualHack: "Use the hack command to manually hack a server",
- TerminalHackingMechanics: "Briefly explain hacking mechanics",
- TerminalCreateScript: "Create a script using nano",
- TerminalTypeScript: "This occurs in the Script Editor page...type the script then save and close",
- TerminalFree: "Use the free command to check RAM",
- TerminalRunScript: "Use the run command to run a script",
- TerminalGoToActiveScriptsPage: "Go to the ActiveScriptsPage",
- ActiveScriptsPage: "Introduction to the Active Scripts Page",
- ActiveScriptsToTerminal: "Go from Active Scripts Page Back to Terminal",
- TerminalTailScript: "Use the tail command to show a script's logs",
- GoToHacknetNodesPage: "Go to the Hacknet Nodes page",
- HacknetNodesIntroduction: "Introduction to Hacknet Nodesm and have user purchase one",
- HacknetNodesGoToWorldPage: "Go to the world page",
- WorldDescription: "Tell the user to explore..theres a lot of different stuff to do out there",
- TutorialPageInfo: "The tutorial page contains a lot of info on different subjects",
- End: "End",
+//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 foodnstuff
+ "TerminalAnalyze", //Analyzing foodnstuff
+ "TerminalNuke", //NUKE foodnstuff
+ "TerminalManualHack", //Hack foodnstuff
+ "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;
}
-var currITutorialStep = iTutorialSteps.Start;
-var iTutorialIsRunning = false;
+var 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();
+ Terminal.resetTerminalInput();
+
//Don't autosave during this interactive tutorial
- Engine.Counters.autoSaveCounter = 999000000000;
+ Engine.Counters.autoSaveCounter = Infinity;
console.log("Interactive Tutorial started");
- currITutorialStep = iTutorialSteps.Start;
- iTutorialIsRunning = true;
+ ITutorial.currStep = 0;
+ ITutorial.isRunning = true;
document.getElementById("interactive-tutorial-container").style.display = "block";
- iTutorialEvaluateStep();
-
//Exit tutorial button
var exitButton = clearEventListeners("interactive-tutorial-exit");
exitButton.addEventListener("click", function() {
@@ -59,142 +81,150 @@ function iTutorialStart() {
//Back button
var backButton = clearEventListeners("interactive-tutorial-back");
- backButton.style.display = "none";
backButton.addEventListener("click", function() {
iTutorialPrevStep();
return false;
});
+
+ //Next button
+ var nextButton = clearEventListeners("interactive-tutorial-next");
+ nextButton.addEventListener("click", function() {
+ iTutorialNextStep();
+ return false;
+ });
+
+ iTutorialEvaluateStep();
}
function iTutorialEvaluateStep() {
- if (!iTutorialIsRunning) {console.log("Interactive Tutorial not running"); return;}
- switch(currITutorialStep) {
+ if (!ITutorial.isRunning) {console.log("Interactive Tutorial not running"); return;}
+
+ //Disable and clear main menu
+ var terminalMainMenu = clearEventListeners("terminal-menu-link");
+ var statsMainMenu = clearEventListeners("stats-menu-link");
+ var activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
+ var hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
+ var cityMainMenu = clearEventListeners("city-menu-link");
+ var 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
+ var 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.");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "inline-block";
- next.addEventListener("click", function() {
- iTutorialNextStep();
- return false;
- });
+ 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";
- //No next button
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "none";
-
- //Flash Character tab
- document.getElementById("stats-menu-link").setAttribute("class", "flashing-button");
-
- //Initialize everything necessary to open the "Character" page
- var charaterMainMenuButton = document.getElementById("stats-menu-link");
- charaterMainMenuButton.addEventListener("click", function() {
+ //Flash 'Stats' menu and set its tutorial click handler
+ statsMainMenu.setAttribute("class", "flashing-button");
+ statsMainMenu.addEventListener("click", function() {
Engine.loadCharacterContent();
iTutorialNextStep(); //Opening the character page will go to the next step
- clearEventListeners("stats-menu-link");
return false;
});
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/multipliers. ")
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "inline-block";
- next.addEventListener("click", function() {
- iTutorialNextStep();
- return false;
- });
+ 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.");
- //No next button
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "none";
+ nextBtn.style.display = "none";
- document.getElementById("terminal-menu-link").setAttribute("class", "flashing-button");
-
- //Initialize everything necessary to open the 'Terminal' Page
- var terminalMainMenuButton = document.getElementById("terminal-menu-link");
- terminalMainMenuButton.addEventListener("click", function() {
+ //Flash 'Terminal' menu and set its tutorial click handler
+ terminalMainMenu.setAttribute("class", "flashing-button");
+ terminalMainMenu.addEventListener("click", function() {
Engine.loadTerminalContent();
iTutorialNextStep();
- clearEventListeners("terminal-menu-link");
return false;
});
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.");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "inline-block";
- next.addEventListener("click", function() {
- iTutorialNextStep();
- return false;
- });
+ 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)");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "none";
- //next step triggered by terminal 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");
- //next step triggered by terminal 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 all of the contents (programs/scripts) " +
"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. ");
- //next step triggered by terminal command
+ "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 either its IP or 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");
- //next step triggered by terminal command
+ 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'.")
- //next step triggered by terminal command
+ 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 [ip/hostname]' command. You can type in " +
"the ip or the hostname, but dont use both.
" +
"From the results of the 'scan-analyze' command, we can see that the 'foodnstuff' server is " +
"only one node away. Let's connect so it now using: 'connect foodnstuff'");
- //next step triggered by terminal command
+ 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");
- //next step triggered by terminal 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 " +
@@ -203,14 +233,16 @@ function iTutorialEvaluateStep() {
"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.");
- //next step triggered by terminal 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.");
- //next step triggered by terminal command
+ 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. Note that 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 " +
@@ -220,25 +252,20 @@ function iTutorialEvaluateStep() {
"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.");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "inline-block";
- next.addEventListener("click", function() {
- iTutorialNextStep();
- return false;
- });
+ 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 foodnstuff.script' after the hack command finishes running (Sidenote: Pressing ctrl + c" +
" will end a command like hack early)");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "none";
- //next step triggered by terminal command
+ nextBtn.style.display = "none"; //next step triggered by terminal command
break;
case iTutorialSteps.TerminalTypeScript:
+ Engine.loadScriptEditorContent("foodnstuff.script", "");
iTutorialSetText("This is the script editor. You can use it to program your scripts. Scripts are " +
"written in the Netscript language, a programming language created for " +
"this game. There are details about the Netscript language in the documentation, which " +
@@ -251,21 +278,24 @@ function iTutorialEvaluateStep() {
"For anyone with basic programming experience, this code should be straightforward. " +
"This script will continuously hack the 'foodnstuff' server.
" +
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.");
- //next step triggered in saveAndCloseScriptEditor() (Script.js)
+ 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.");
- //next step triggered by terminal commmand
+ nextBtn.style.display = "none"; //next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalRunScript:
+ Engine.loadTerminalContent();
iTutorialSetText("We have 16GB of free RAM on this machine, which is enough to run our " +
"script. Let's run our script using 'run foodnstuff.script'.");
- //next step triggered by terminal commmand
+ nextBtn.style.display = "none"; //next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
+ Engine.loadTerminalContent();
iTutorialSetText("Your script is now running! The script might take a few seconds to 'fully start up'. " +
"Your scripts will continuously run in the background and will automatically stop if " +
"the code ever completes (the 'foodnstuff.script' will never complete because it " +
@@ -274,117 +304,114 @@ function iTutorialEvaluateStep() {
"much slower rate.
" +
"Let's check out some statistics for our running scripts by clicking the " +
"'Active Scripts' link in the main navigation menu.");
- document.getElementById("active-scripts-menu-link").setAttribute("class", "flashing-button");
- var activeScriptsMainMenuButton = document.getElementById("active-scripts-menu-link");
- activeScriptsMainMenuButton.addEventListener("click", function() {
+ nextBtn.style.display = "none";
+
+ //Flash 'Active Scripts' menu and set its tutorial click handler
+ activeScriptsMainMenu.setAttribute("class", "flashing-button");
+ activeScriptsMainMenu.addEventListener("click", function() {
Engine.loadActiveScriptsContent();
iTutorialNextStep();
- clearEventListeners("active-scripts-menu-link");
return false;
});
break;
case iTutorialSteps.ActiveScriptsPage:
+ Engine.loadActiveScriptsContent();
iTutorialSetText("This page displays stats/information about all of your scripts that are " +
"running across every existing server. You can use this to gauge how well " +
"your scripts are doing. Let's go back to the Terminal now using the 'Terminal'" +
"link.");
- document.getElementById("terminal-menu-link").setAttribute("class", "flashing-button");
- //Initialize everything necessary to open the 'Terminal' Page
- var terminalMainMenuButton = clearEventListeners("terminal-menu-link");
- terminalMainMenuButton.addEventListener("click", function() {
+ nextBtn.style.display = "none";
+
+ //Flash 'Terminal' button and set its tutorial click handler
+ terminalMainMenu.setAttribute("class", "flashing-button");
+ terminalMainMenu.addEventListener("click", function() {
Engine.loadTerminalContent();
iTutorialNextStep();
- clearEventListeners("terminal-menu-link");
return false;
});
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 foodnstuff.script'");
- //next step triggered by terminal command
+ 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 pretty much covers the basics of hacking. To learn more about writing " +
"scripts using the Netscript language, select the 'Tutorial' link in the " +
- "main navigation menu to look at the documentation. For now, let's move on " +
- "to something else!");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "inline-block";
- next.addEventListener("click", function() {
- iTutorialNextStep();
- return false;
- });
+ "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.
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 Nodes' page through the main navigation menu now.");
- document.getElementById("hacknet-nodes-menu-link").setAttribute("class", "flashing-button");
- var hacknetNodesButton = clearEventListeners("hacknet-nodes-menu-link");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "none";
- hacknetNodesButton.addEventListener("click", function() {
+ nextBtn.style.display = "none";
+
+ //Flash 'Hacknet' menu and set its tutorial click handler
+ hacknetMainMenu.setAttribute("class", "flashing-button");
+ hacknetMainMenu.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
iTutorialNextStep();
- clearEventListeners("hacknet-nodes-menu-link");
return false;
});
break;
case iTutorialSteps.HacknetNodesIntroduction:
+ Engine.loadHacknetNodesContent();
iTutorialSetText("From this page you can purchase new Hacknet Nodes and upgrade your " +
"existing ones. Let's purchase a new one now.");
- //Next step triggered by purchaseHacknet() (HacknetNode.js)
+ 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.");
- document.getElementById("city-menu-link").setAttribute("class", "flashing-button");
- var worldButton = clearEventListeners("city-menu-link");
- worldButton.addEventListener("click", function() {
+ nextBtn.style.display = "none";
+
+ //Flash 'City' menu and set its tutorial click handler
+ cityMainMenu.setAttribute("class", "flashing-button");
+ cityMainMenu.addEventListener("click", function() {
Engine.loadWorldContent();
iTutorialNextStep();
- clearEventListeners("city-menu-link");
return false;
});
break;
case iTutorialSteps.WorldDescription:
+ Engine.loadWorldContent();
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.");
- document.getElementById("tutorial-menu-link").setAttribute("class", "flashing-button");
- var tutorialButton = clearEventListeners("tutorial-menu-link");
- tutorialButton.addEventListener("click", function() {
+ nextBtn.style.display = "none";
+
+ //Flash 'Tutorial' menu and set its tutorial click handler
+ tutorialMainMenu.setAttribute("class", "flashing-button");
+ tutorialMainMenu.addEventListener("click", function() {
Engine.loadTutorialContent();
iTutorialNextStep();
- clearEventListeners("tutorial-menu-link");
return false;
});
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!");
- var next = clearEventListeners("interactive-tutorial-next");
- next.style.display = "inline-block";
- next.innerHTML = "Finish Tutorial";
-
- var backButton = clearEventListeners("interactive-tutorial-back");
- backButton.style.display = "none";
-
- next.addEventListener("click", function() {
- iTutorialNextStep();
- return false;
- });
+ nextBtn.style.display = "inline-block";
+ nextBtn.innerHTML = "Finish Tutorial";
break;
case iTutorialSteps.End:
iTutorialEnd();
@@ -392,264 +419,85 @@ function iTutorialEvaluateStep() {
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() {
- switch(currITutorialStep) {
- case iTutorialSteps.Start:
- currITutorialStep = iTutorialSteps.GoToCharacterPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.GoToCharacterPage:
+ //Special behavior for certain steps
+ if (ITutorial.currStep === iTutorialSteps.GoToCharacterPage) {
document.getElementById("stats-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.CharacterPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.CharacterPage:
- currITutorialStep = iTutorialSteps.CharacterGoToTerminalPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.CharacterGoToTerminalPage:
- document.getElementById("terminal-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.TerminalIntro;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalIntro:
- currITutorialStep = iTutorialSteps.TerminalHelp;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalHelp:
- currITutorialStep = iTutorialSteps.TerminalLs;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalLs:
- currITutorialStep = iTutorialSteps.TerminalScan;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalScan:
- currITutorialStep = iTutorialSteps.TerminalScanAnalyze1;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalScanAnalyze1:
- currITutorialStep = iTutorialSteps.TerminalScanAnalyze2;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalScanAnalyze2:
- currITutorialStep = iTutorialSteps.TerminalConnect;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalConnect:
- currITutorialStep = iTutorialSteps.TerminalAnalyze;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalAnalyze:
- currITutorialStep = iTutorialSteps.TerminalNuke;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalNuke:
- currITutorialStep = iTutorialSteps.TerminalManualHack;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalManualHack:
- currITutorialStep = iTutorialSteps.TerminalHackingMechanics;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalHackingMechanics:
- currITutorialStep = iTutorialSteps.TerminalCreateScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalCreateScript:
- currITutorialStep = iTutorialSteps.TerminalTypeScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalTypeScript:
- currITutorialStep = iTutorialSteps.TerminalFree;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalFree:
- currITutorialStep = iTutorialSteps.TerminalRunScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalRunScript:
- currITutorialStep = iTutorialSteps.TerminalGoToActiveScriptsPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalGoToActiveScriptsPage:
- document.getElementById("active-scripts-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.ActiveScriptsPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.ActiveScriptsPage:
- document.getElementById("terminal-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.ActiveScriptsToTerminal;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.ActiveScriptsToTerminal:
- currITutorialStep = iTutorialSteps.TerminalTailScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalTailScript:
- currITutorialStep = iTutorialSteps.GoToHacknetNodesPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.GoToHacknetNodesPage:
- document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.HacknetNodesIntroduction;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.HacknetNodesIntroduction:
- currITutorialStep = iTutorialSteps.HacknetNodesGoToWorldPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.HacknetNodesGoToWorldPage:
- document.getElementById("city-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.WorldDescription;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.WorldDescription:
- document.getElementById("tutorial-menu-link").removeAttribute("class");
- currITutorialStep = iTutorialSteps.TutorialPageInfo;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TutorialPageInfo:
- currITutorialStep = iTutorialSteps.End;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.End:
- break;
- default:
- throw new Error("Invalid tutorial step");
}
+ if (ITutorial.currStep === iTutorialSteps.CharacterGoToTerminalPage) {
+ document.getElementById("terminal-menu-link").removeAttribute("class");
+ }
+ if (ITutorial.currStep === iTutorialSteps.TerminalGoToActiveScriptsPage) {
+ document.getElementById("active-scripts-menu-link").removeAttribute("class");
+ }
+ if (ITutorial.currStep === iTutorialSteps.ActiveScriptsPage) {
+ document.getElementById("terminal-menu-link").removeAttribute("class");
+ }
+ if (ITutorial.currStep === iTutorialSteps.GoToHacknetNodesPage) {
+ document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
+ }
+ if (ITutorial.currStep === iTutorialSteps.HacknetNodesGoToWorldPage) {
+ document.getElementById("city-menu-link").removeAttribute("class");
+ }
+ if (ITutorial.currStep === iTutorialSteps.WorldDescription) {
+ document.getElementById("tutorial-menu-link").removeAttribute("class");
+ }
+
+ ITutorial.stepIsDone[ITutorial.currStep] = true;
+ if (ITutorial.currStep < iTutorialSteps.End) {
+ ITutorial.currStep += 1;
+ }
+ iTutorialEvaluateStep();
}
//Go to previous step and evaluate
function iTutorialPrevStep() {
- switch(currITutorialStep) {
- case iTutorialSteps.Start:
- currITutorialStep = iTutorialSteps.Start;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.GoToCharacterPage:
- currITutorialStep = iTutorialSteps.Start;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.CharacterPage:
- currITutorialStep = iTutorialSteps.GoToCharacterPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.CharacterGoToTerminalPage:
- currITutorialStep = iTutorialSteps.CharacterPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalIntro:
- currITutorialStep = iTutorialSteps.CharacterGoToTerminalPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalHelp:
- currITutorialStep = iTutorialSteps.TerminalIntro;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalLs:
- currITutorialStep = iTutorialSteps.TerminalHelp;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalScan:
- currITutorialStep = iTutorialSteps.TerminalLs;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalConnect:
- currITutorialStep = iTutorialSteps.TerminalScan;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalAnalyze:
- currITutorialStep = iTutorialSteps.TerminalConnect;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalNuke:
- currITutorialStep = iTutorialSteps.TerminalAnalyze;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalManualHack:
- currITutorialStep = iTutorialSteps.TerminalNuke;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalHackingMechanics:
- currITutorialStep = iTutorialSteps.TerminalManualHack;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalCreateScript:
- currITutorialStep = iTutorialSteps.TerminalManualHack;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalTypeScript:
- currITutorialStep = iTutorialSteps.TerminalCreateScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalFree:
- currITutorialStep = iTutorialSteps.TerminalTypeScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalRunScript:
- currITutorialStep = iTutorialSteps.TerminalFree;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalGoToActiveScriptsPage:
- currITutorialStep = iTutorialSteps.TerminalRunScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.ActiveScriptsPage:
- currITutorialStep = iTutorialSteps.TerminalGoToActiveScriptsPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.ActiveScriptsToTerminal:
- currITutorialStep = iTutorialSteps.ActiveScriptsPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TerminalTailScript:
- currITutorialStep = iTutorialSteps.ActiveScriptsToTerminal;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.GoToHacknetNodesPage:
- currITutorialStep = iTutorialSteps.TerminalTailScript;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.HacknetNodesIntroduction:
- currITutorialStep = iTutorialSteps.GoToHacknetNodesPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.HacknetNodesGoToWorldPage:
- currITutorialStep = iTutorialSteps.HacknetNodesIntroduction;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.WorldDescription:
- currITutorialStep = iTutorialSteps.HacknetNodesGoToWorldPage;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.TutorialPageInfo:
- currITutorialStep = iTutorialSteps.WorldDescription;
- iTutorialEvaluateStep();
- break;
- case iTutorialSteps.End:
- break;
- default:
- throw new Error("Invalid tutorial step");
+ if (ITutorial.currStep > iTutorialSteps.Start) {
+ ITutorial.currStep -= 1;
}
+ iTutorialEvaluateStep();
}
function iTutorialEnd() {
//Re-enable auto save
- Engine.Counters.autoSaveCounter = 300;
+ if (Settings.AutosaveInterval === 0) {
+ Engine.Counters.autoSaveCounter = Infinity;
+ } else {
+ Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
+ }
+
console.log("Ending interactive tutorial");
Engine.init();
- currITutorialStep = iTutorialSteps.End;
- iTutorialIsRunning = false;
+ ITutorial.currStep = iTutorialSteps.End;
+ ITutorial.isRunning = false;
document.getElementById("interactive-tutorial-container").style.display = "none";
- dialogBoxCreate("If you are new to the game, the following links may be useful for you!
" +
- "Getting Started Guide" +
- "Wiki
" +
- "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 hackers-starting-handbook.lit");
+
+ //Create a popup with final introductory stuff
+ var popupId = "interactive-tutorial-ending-popup";
+ var txt = createElement("p", {
+ innerHTML:
+ "If you are new to the game, the following links may be useful for you!
" +
+ "Getting Started Guide" +
+ "Wiki" +
+ "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 hackers-starting-handbook.lit"
+ });
+ var 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("hackers-starting-handbook.lit");
}
@@ -660,5 +508,4 @@ function iTutorialSetText(txt) {
textBox.parentElement.scrollTop = 0; // this resets scroll position
}
-export {iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, currITutorialStep,
- iTutorialIsRunning};
+export {iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, ITutorial};
diff --git a/src/Literature.js b/src/Literature.js
index bb80e6a70..c38c2e1b8 100644
--- a/src/Literature.js
+++ b/src/Literature.js
@@ -23,7 +23,11 @@ function initLiterature() {
var title, fn, txt;
title = "The Beginner's Guide to Hacking";
fn = "hackers-starting-handbook.lit";
- txt = "When starting out, hacking is the most profitable way to earn money and progress. This " +
+ txt = "Some resources:
" +
+ "Learn to Program
" +
+ "For Experienced JavaScript Developers: NetscriptJS
" +
+ "Netscript Documentation
" +
+ "When starting out, hacking is the most profitable way to earn money and progress. This " +
"is a brief collection of tips/pointers on how to make the most out of your hacking scripts.
" +
"-hack() and grow() both work by percentages. hack() steals a certain percentage of the " +
"money on a server, and grow() increases the amount of money on a server by some percentage (multiplicatively)
" +
diff --git a/src/Player.js b/src/Player.js
index 03ea14a7c..491e5d078 100644
--- a/src/Player.js
+++ b/src/Player.js
@@ -566,6 +566,8 @@ PlayerObject.prototype.analyze = function() {
PlayerObject.prototype.hasProgram = function(programName) {
var home = Player.getHomeComputer();
+ if (home == null) {return false;}
+
for (var i = 0; i < home.programs.length; ++i) {
if (programName.toLowerCase() == home.programs[i].toLowerCase()) {return true;}
}
diff --git a/src/Script.js b/src/Script.js
index fca23dc83..670ff2cd7 100644
--- a/src/Script.js
+++ b/src/Script.js
@@ -21,7 +21,7 @@ import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings} from "./Fconf";
import {iTutorialSteps, iTutorialNextStep,
- iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial";
+ ITutorial} from "./InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript,
@@ -281,8 +281,9 @@ function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
- if (iTutorialIsRunning && currITutorialStep == iTutorialSteps.TerminalTypeScript) {
- if (filename != "foodnstuff.script") {
+ if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
+ //Make sure filename + code properly follow tutorial
+ if (filename !== "foodnstuff.script") {
dialogBoxCreate("Leave the script name as 'foodnstuff'!");
return;
}
@@ -291,7 +292,23 @@ function saveAndCloseScriptEditor() {
dialogBoxCreate("Please copy and paste the code from the tutorial!");
return;
}
- iTutorialNextStep();
+
+ //Save the script
+ let s = Player.getCurrentServer();
+ for (var i = 0; i < s.scripts.length; i++) {
+ if (filename == s.scripts[i].filename) {
+ s.scripts[i].saveScript();
+ Engine.loadTerminalContent();
+ return iTutorialNextStep();
+ }
+ }
+
+ //If the current script does NOT exist, create a new one
+ let script = new Script();
+ script.saveScript();
+ s.scripts.push(script);
+
+ return iTutorialNextStep();
}
if (filename == "") {
diff --git a/src/Terminal.js b/src/Terminal.js
index 821416ac2..d87277a60 100644
--- a/src/Terminal.js
+++ b/src/Terminal.js
@@ -12,8 +12,7 @@ import {FconfSettings, parseFconfSettings,
createFconf} from "./Fconf";
import {TerminalHelpText, HelpTexts} from "./HelpText";
import {iTutorialNextStep, iTutorialSteps,
- iTutorialIsRunning,
- currITutorialStep} from "./InteractiveTutorial";
+ ITutorial} from "./InteractiveTutorial";
import {showLiterature} from "./Literature";
import {showMessage, Message} from "./Message";
import {scriptCalculateHackingTime,
@@ -762,11 +761,11 @@ let Terminal = {
if (commandArray.length == 0) {return;}
/****************** Interactive Tutorial Terminal Commands ******************/
- if (iTutorialIsRunning) {
+ if (ITutorial.isRunning) {
var foodnstuffServ = GetServerByHostname("foodnstuff");
if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;}
- switch(currITutorialStep) {
+ switch(ITutorial.currStep) {
case iTutorialSteps.TerminalHelp:
if (commandArray[0] == "help") {
post(TerminalHelpText);
@@ -1809,7 +1808,7 @@ let Terminal = {
* @returns {void}
*/
/**
- * @type {Object. {