From 8b99e46d5e5c50525e89bc6922216e80cd75e178 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Wed, 29 Aug 2018 14:06:21 -0500 Subject: [PATCH] Updated Interactive Tutorial and added a few helpful links to the hackers-starting-handbook Literature file --- css/interactivetutorial.scss | 22 +- src/HacknetNode.js | 6 +- src/InteractiveTutorial.js | 631 +++++++++++++---------------------- src/Literature.js | 6 +- src/Player.js | 2 + src/Script.js | 25 +- src/Terminal.js | 9 +- 7 files changed, 292 insertions(+), 409 deletions(-) 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 8f1faa218..94c943591 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 48d4771a5..b552f144e 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. {