Implemented interactive tutorial!

This commit is contained in:
Daniel Xie 2017-05-06 01:24:01 -05:00
parent e2cbc38920
commit 2e55f1d472
10 changed files with 558 additions and 187 deletions

@ -2,20 +2,23 @@
#interactive-tutorial-container {
display: none;
position: fixed; /* Stay in place */
left: 0;
right: 0;
top: 0;
height: 100%; /* Full height */
margin: 20% auto;
padding: 10px;
border: 5px solid #FFFFFF;
width: 40%;
width: 35%;
overflow: auto; /* Enable scroll if needed */
background-color: black; /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
background-color: #444; /* Fallback color */
color: white;
}
#interactive-tutorial-text {
padding: 4px;
margin: 4px;
color: white;
background-color: #444;
}
#interactive-tutorial-exit,

@ -54,6 +54,7 @@
<script src="src/Prestige.js"></script>
<script src="src/SaveObject.js"></script>
<script src="src/DarkWeb.js"></script>
<script src="src/InteractiveTutorial.js"></script>
<script src="src/engine.js"></script>
@ -659,8 +660,8 @@
<!-- Interactive Tutorial Text Screen -->
<div id="interactive-tutorial-container">
<p id="interactive-tutorial-text"> </p>
<span id="interactive-tutorial-exit"> Exit tutorial </span>
<span id="interactive-tutorial-next"> Continue </span>
<span id="interactive-tutorial-exit"> Exit Tutorial </span>
<span id="interactive-tutorial-next"> Next </span>
</div>
</body>

@ -104,6 +104,7 @@ CONSTANTS = {
"rm Delete a script/program from the machine. (WARNING: Permanent)<br>" +
"run [script/program] Execute a program or a script<br>" +
"scan See 'netstat' command<br>" +
"sudov Shows whether or not you have root access on this computer<br>" +
"tail [script] Display script logs (logs contain details about active scripts)" +
"telnet [ip/hostname] See 'connect' command<br>" +
"top Display all running scripts and their RAM usage<br>",

@ -47,7 +47,7 @@ listAllDarkwebItems = function() {
}
buyDarkwebItem = function(itemName) {
if (DarkWebItems.BruteSSHProgram.beginsWith(itemName)) {
if (DarkWebItems.BruteSSHProgram.startsWith(itemName)) {
var price = parseDarkwebItemPrice(DarkWebItems.BruteSSHProgram);
if (price > 0 && Player.money >= price) {
Player.loseMoney(price);
@ -57,7 +57,7 @@ buyDarkwebItem = function(itemName) {
} else {
post("Not enough money to purchase " + itemName);
}
} else if (DarkWebItems.FTPCrackProgram.beginsWith(itemName)) {
} else if (DarkWebItems.FTPCrackProgram.startsWith(itemName)) {
var price = parseDarkwebItemPrice(DarkWebItems.FTPCrackProgram);
if (price > 0 && Player.money >= price) {
Player.loseMoney(price);
@ -67,7 +67,7 @@ buyDarkwebItem = function(itemName) {
} else {
post("Not enough money to purchase " + itemName);
}
} else if (DarkWebItems.RelaySMTPProgram.beginsWith(itemName)) {
} else if (DarkWebItems.RelaySMTPProgram.startsWith(itemName)) {
var price = parseDarkwebItemPrice(DarkWebItems.RelaySMTPProgram);
if (price > 0 && Player.money >= price) {
Player.loseMoney(price);
@ -77,7 +77,7 @@ buyDarkwebItem = function(itemName) {
} else {
post("Not enough money to purchase " + itemName);
}
} else if (DarkWebItems.HTTPWormProgram.beginsWith(itemName)) {
} else if (DarkWebItems.HTTPWormProgram.startsWith(itemName)) {
var price = parseDarkwebItemPrice(DarkWebItems.HTTPWormProgram);
if (price > 0 && Player.money >= price) {
Player.loseMoney(price);
@ -87,7 +87,7 @@ buyDarkwebItem = function(itemName) {
} else {
post("Not enough money to purchase " + itemName);
}
} else if (DarkWebItems.SQLInjectProgram.beginsWith(itemName)) {
} else if (DarkWebItems.SQLInjectProgram.startsWith(itemName)) {
var price = parseDarkwebItemPrice(DarkWebItems.SQLInjectProgram);
if (price > 0 && Player.money >= price) {
Player.loseMoney(price);

@ -88,6 +88,17 @@ Reviver.constructors.HacknetNode = HacknetNode;
purchaseHacknet = function() {
/* INTERACTIVE TUTORIAL */
if (iTutorialIsRunning) {
if (currITutorialStep == iTutorialSteps.HacknetNodesIntroduction) {
iTutorialNextStep();
} else {
return;
}
}
/* END INTERACTIVE TUTORIAL */
var cost = getCostOfNextHacknetNode();
if (isNaN(cost)) {throw new Error("Cost is NaN"); return;}
if (cost > Player.money) {
@ -117,9 +128,10 @@ getCostOfNextHacknetNode = function() {
//Creates Hacknet Node DOM elements when the page is opened
displayHacknetNodesContent = function() {
//Update Hacknet Nodes button
var purchaseButton = document.getElementById("hacknet-nodes-purchase-button");
var newPurchaseButton = purchaseButton.cloneNode(true);
purchaseButton.parentNode.replaceChild(newPurchaseButton, purchaseButton);
var newPurchaseButton = clearEventListeners("hacknet-nodes-purchase-button");
//var purchaseButton = document.getElementById("hacknet-nodes-purchase-button");
//var newPurchaseButton = purchaseButton.cloneNode(true);
//purchaseButton.parentNode.replaceChild(newPurchaseButton, purchaseButton);
newPurchaseButton.addEventListener("click", function() {
purchaseHacknet();

@ -13,18 +13,17 @@ iTutorialSteps = {
TerminalNuke: "Use the NUKE Program to gain root access to a server",
TerminalManualHack: "Use the hack command to manually hack a server",
TerminalCreateScript: "Create a script using nano",
TerminalTypeScript: "This occurs in the Script Editor page...type the script then save and close","
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 Nodes",
HacknetNodesPurchase: "Have the user purchase a Hacknet Node",
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",
WorldGoToTutorialPage: "Go to the Tutorial Page",
TutorialPageInfo: "The tutorial page contains a lot of info on different subjects",
End: "End",
}
@ -33,24 +32,32 @@ var currITutorialStep = iTutorialSteps.Start;
var iTutorialIsRunning = false;
function iTutorialStart() {
//Don't autosave during this interactive tutorial
Engine.Counters.autoSaveCounter = 999000000000;
console.log("Interactive Tutorial started");
currITutorialStep = iTutorialSteps.Start;
iTutorialIsRunning = true;
document.getElementById("interactive-tutorial-container").style.display = "block";
iTutorialEvaluateStep();
//Exit tutorial button
var exitButton = clearEventListeners("interactive-tutorial-exit");
exitButton.addEventListener("click", function() {
iTutorialEnd();
return false;
});
}
function iTutorialEvaluateStep() {
if (!iTutorialIsRunning) {console.log("Interactive Tutorial not running"); return;}
switch(currITutorialStep) {
case iTutorialSteps.Start:
iTutorialSetText("Welcome to Bitburner, a cyberpunk-themed incremental RPG! <br><br>" +
iTutorialSetText("Welcome to Bitburner, a cyberpunk-themed incremental RPG! " +
"The game takes place in a dark, dystopian future...The year is 2077...<br><br>" +
"This tutorial will show you the basics of the game to help you get started. " +
"You may skip the tutorial at any time");
"You may skip the tutorial at any time.");
var next = clearEventListeners("interactive-tutorial-next");
next.addEventListener("click", function() {
iTutorialNextStep();
@ -66,10 +73,11 @@ function iTutorialEvaluateStep() {
next.style.display = "none";
//Initialize everything necessary to open the "Character" page
Engine.Clickables.characterMainMenuButton = document.getElementById("character-menu-link");
Engine.Clickables.characterMainMenuButton.addEventListener("click", function() {
var charaterMainMenuButton = document.getElementById("character-menu-link");
charaterMainMenuButton.addEventListener("click", function() {
Engine.loadCharacterContent();
iTutorialNextStep(); //Opening the character page will go to the next step
clearEventListeners("character-menu-link");
return false;
});
break;
@ -91,58 +99,232 @@ function iTutorialEvaluateStep() {
next.style.display = "none";
//Initialize everything necessary to open the 'Terminal' Page
Engine.Clickables.terminalMainMenuButton = document.getElementById("terminal-menu-link");
Engine.Clickables.terminalMainMenuButton.addEventListener("click", function() {
var terminalMainMenuButton = document.getElementById("terminal-menu-link");
terminalMainMenuButton.addEventListener("click", function() {
Engine.loadTerminalContent();
iTutorialNextStep();
clearEventListeners("terminal-menu-link");
return false;
});
break;
case iTutorialSteps.TerminalIntro:
iTutorialSetText("The Terminal is used to interface with your home computer as well as " +
"all of the other machines around the world. A lot of content in the game is " +
"accessible only through the Terminal, and is necessary for progressing. ");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
break;
case iTutorialSteps.TerminalHelp:
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
break;
case iTutorialSteps.TerminalLs:
iTutorialSetText("The 'help' command displays a list of all available commands, how to use them, " +
"and a description of what they do. <br><br>Let's try another command. Enter the 'ls' command");
//next step triggered by terminal command
break;
case iTutorialSteps.TerminalScan:
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. <br><br> Through 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 (Alternatively, you can also enter the 'netstat' command " +
"which does the same thing). ");
//next step triggered by terminal command
break;
case iTutorialSteps.TerminalConnect:
iTutorialSetText("The 'scan/netstat' 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. <br><br> " +
"To connect to a machine, use the 'connect [ip/hostname]' command. You can type in " +
"the ip or the hostname, but dont use both. (Alternatively, " +
"the 'telnet [ip/hostname]' command does the same thing).<br><br>" +
"Let's try this now by connecting to the 'foodnstuff' server (connect foodnstuff)");
//next step triggered by terminal command
break;
case iTutorialSteps.TerminalAnalyze:
iTutorialSetText("You are now connected to another machine! What can you do now? You can hack it!<br><br> 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. <br><br> " +
"Before you try to hack a server, you should run diagnostics using the 'analyze' command");
//next step triggered by terminal command
break;
case iTutorialSteps.TerminalNuke:
iTutorialSetText("When the 'analyze' command finishes running it will show useful information " +
"about hacking the server. <br><br> For this server, the required hacking skill is only 1, " +
"which means you are able to 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.<br><br> 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
break;
case iTutorialSteps.TerminalManualHack:
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
break;
case iTutorialSteps.TerminalCreateScript:
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 percentage is determined by a variety of factors, including " +
"your hacking skill and the server's security level. <br><br>" +
"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! <br><br>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)");
//next step triggered by terminal command
break;
case iTutorialSteps.TerminalTypeScript:
iTutorialSetText("This is the script editor. You can use it to program your scripts. Scripts are " +
"written in the Netscript language, a very simple programming language created for " +
"this game. There are details about the Netscript language in the documentation, which " +
"can be accessed in the 'Tutorial' tab on the main navigation menu. For now, just copy " +
"and paste the following code into the script editor: <br><br>" +
"while(true) { <br>" +
"hack('foodnstuff'); <br>" +
"}<br><br> " +
"For anyone with basic programming experience, this code should be straightforward. " +
"This script will continuously hack the 'foodnstuff' server. <br><br>" +
"To save and close the script editor, press the button in the top left, or press ctrl + b.");
//next step triggered in saveAndCloseScriptEditor() (Script.js)
break;
case iTutorialSteps.TerminalFree:
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. <br><br> To check how much " +
"RAM is available on this machine, enter the 'free' command.");
//next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalRunScript:
iTutorialSetText("We have 2GB 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
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
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 " +
"runs an infinite loop). <br><br>These scripts will passively earn you income and hacking experience. " +
"Your scripts will also earn money and experience while you are offline, although at a " +
"much slower rate. <br><br> " +
"Let's check out some statistics of our active, running scripts by clicking the " +
"'Active Scripts' link in the main navigation menu. ");
var activeScriptsMainMenuButton = document.getElementById("active-scripts-menu-link");
activeScriptsMainMenuButton.addEventListener("click", function() {
Engine.loadActiveScriptsContent();
iTutorialNextStep();
clearEventListeners("active-scripts-menu-link");
return false;
});
break;
case iTutorialSteps.ActiveScriptsPage:
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.");
//Initialize everything necessary to open the 'Terminal' Page
var terminalMainMenuButton = clearEventListeners("terminal-menu-link");
terminalMainMenuButton.addEventListener("click", function() {
Engine.loadTerminalContent();
iTutorialNextStep();
clearEventListeners("terminal-menu-link");
return false;
});
break;
case iTutorialSteps.ActiveScriptsToTerminal:
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
break;
case iTutorialSteps.TerminalTailScript:
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! <br><br>" +
"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;
});
break;
case iTutorialSteps.GoToHacknetNodesPage:
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.");
var hacknetNodesButton = clearEventListeners("hacknet-nodes-menu-link");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "none";
hacknetNodesButton.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
iTutorialNextStep();
clearEventListeners("hacknet-nodes-menu-link");
return false;
});
break;
case iTutorialSteps.HacknetNodesIntroduction:
break;
case iTutorialSteps.HacknetNodesPurchase:
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)
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
iTutorialSetText("You just purchase 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. <br><br>" +
"Let's go to the 'World' page through the main navigation menu.");
var worldButton = clearEventListeners("world-menu-link");
worldButton.addEventListener("click", function() {
Engine.loadWorldContent();
iTutorialNextStep();
clearEventListeners("world-menu-link");
return false;
});
break;
case iTutorialSteps.WorldDescription:
iTutorialSetText("This page lists all of the different locations you can currently " +
"travel to. Each location has something that can you do. " +
"There's a lot of content out in the world, make sure " +
"you explore and discover!<br><br>" +
"Lastly, click on the 'Tutorial' link in the main navigation menu.");
var tutorialButton = clearEventListeners("tutorial-menu-link");
tutorialButton.addEventListener("click", function() {
Engine.loadTutorialContent();
iTutorialNextStep();
clearEventListeners("tutorial-menu-link");
return false;
});
break;
case iTutorialSteps.WorldGoToTutorialPage:
break;
case iTutorialSteps.TutorialPageInfo:
iTutorialSetText("This page contains a lot of different documentation about the game's " +
"content and mechanics. If you have any questions about how something works " +
"make sure you check this out first. That's the end of the tutorial. " +
"Hope you enjoy the game!");
var next = clearEventListeners("interactive-tutorial-next");
next.style.display = "inline-block";
next.addEventListener("click", function() {
iTutorialNextStep();
return false;
});
break;
case iTutorialSteps.End:
iTutorialEnd();
break;
default:
throw new Error("Invalid tutorial step");
@ -151,6 +333,7 @@ function iTutorialEvaluateStep() {
//Go to the next step and evaluate it
function iTutorialNextStep() {
console.log("iTutorialNextStep() called with current step: " + currITutorialStep);
switch(currITutorialStep) {
case iTutorialSteps.Start:
currITutorialStep = iTutorialSteps.GoToCharacterPage;
@ -205,6 +388,10 @@ function iTutorialNextStep() {
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalTypeScript:
currITutorialStep = iTutorialSteps.TerminalFree;
iTutorialEvaluateStep();
break;
case iTutorialSteps.TerminalFree:
currITutorialStep = iTutorialSteps.TerminalRunScript;
iTutorialEvaluateStep();
break;
@ -233,10 +420,6 @@ function iTutorialNextStep() {
iTutorialEvaluateStep();
break;
case iTutorialSteps.HacknetNodesIntroduction:
currITutorialStep = iTutorialSteps.HacknetNodesPurchase;
iTutorialEvaluateStep();
break;
case iTutorialSteps.HacknetNodesPurchase:
currITutorialStep = iTutorialSteps.HacknetNodesGoToWorldPage;
iTutorialEvaluateStep();
break;
@ -245,10 +428,6 @@ function iTutorialNextStep() {
iTutorialEvaluateStep();
break;
case iTutorialSteps.WorldDescription:
currITutorialStep = iTutorialSteps.WorldGoToTutorialPage;
iTutorialEvaluateStep();
break;
case iTutorialSteps.WorldGoToTutorialPage:
currITutorialStep = iTutorialSteps.TutorialPageInfo;
iTutorialEvaluateStep();
break;
@ -264,8 +443,13 @@ function iTutorialNextStep() {
}
function iTutorialEnd() {
//Re-enable auto save
Engine.Counters.autoSaveCounter = 1;
console.log("Ending interactive tutorial");
Engine.init();
currITutorialStep = iTutorialSteps.End;
iTutorialIsRunning = false;
document.getElementById("interactive-tutorial-container").style.display = "none";
}
function iTutorialSetText(txt) {

@ -17,7 +17,7 @@ document.addEventListener("DOMContentLoaded", scriptEditorSaveCloseInit, false);
//Define key commands in script editor (ctrl o to save + close, etc.)
$(document).keydown(function(e) {
if (Engine.currentPage == Engine.Page.ScriptEditor) {
//Ctrl + x
//Ctrl + b
if (e.keyCode == 66 && e.ctrlKey) {
saveAndCloseScriptEditor();
}
@ -26,6 +26,20 @@ $(document).keydown(function(e) {
function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value;
if (iTutorialIsRunning && currITutorialStep == iTutorialSteps.TerminalTypeScript) {
if (filename != "foodnstuff") {
dialogBoxCreate("Leave the script name as 'foodnstuff'!");
return;
}
var code = document.getElementById("script-editor-text").value;
code = code.replace(/\s\s+/g, '');
console.log(code);
if (code.indexOf("while(true) {hack('foodnstuff');}") == -1) {
dialogBoxCreate("Please copy and paste the code from the tutorial!");
return;
}
iTutorialNextStep();
}
if (filename == "") {
//If no filename...just close and do nothing

@ -40,7 +40,6 @@ $(document).keydown(function(event) {
if (command.length > 0) {
post("> " + command);
//TODO Do i have to switch the order of these two?
Terminal.executeCommand(command);
$('input[class=terminal-input]').val("");
}
@ -289,12 +288,16 @@ var Terminal = {
finishAnalyze: function(cancelled = false) {
if (cancelled == false) {
post(Player.getCurrentServer().hostname + ": ");
var rootAccess = "";
if (Player.getCurrentServer().hasAdminRights) {rootAccess = "YES";}
else {rootAccess = "NO";}
post("Root Access: " + rootAccess);
post("Required hacking skill: " + Player.getCurrentServer().requiredHackingSkill);
//TODO Make these actual estimates by adding a random offset to result?
//TODO Change the text to sound better
post("Estimated chance to hack: " + Math.round(Player.calculateHackingChance() * 100) + "%");
post("Estimated time to hack: " + Math.round(Player.calculateHackingTime()) + " seconds");
post("Estimed total money available on server: $" + Player.getCurrentServer().moneyAvailable);
post("Estimated chance to hack: " + formatNumber(addOffset(Player.calculateHackingChance() * 100, 5), 2) + "%");
post("Estimated time to hack: " + formatNumber(addOffset(Player.calculateHackingTime(), 5), 3) + " seconds");
post("Estimated total money available on server: $" + formatNumber(addOffset(Player.getCurrentServer().moneyAvailable, 5), 2));
post("Required number of open ports for NUKE: " + Player.getCurrentServer().numOpenPortsRequired);
if (Player.getCurrentServer().sshPortOpen) {
post("SSH port: Open")
@ -349,7 +352,126 @@ var Terminal = {
var commandArray = command.split(" ");
if (commandArray.length == 0) {return;}
/****************** Interactive Tutorial Terminal Commands ******************/
if (iTutorialIsRunning) {
var foodnstuffServ = GetServerByHostname("foodnstuff");
if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;}
switch(currITutorialStep) {
case iTutorialSteps.TerminalHelp:
if (commandArray[0] == "help") {
post(CONSTANTS.HelpText);
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.TerminalLs:
if (commandArray[0] == "ls") {
Terminal.executeListCommand(commandArray);
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.TerminalScan:
if (commandArray[0] == "scan") {
Terminal.executeScanCommand(commandArray);
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.TerminalConnect:
if (commandArray.length == 2) {
if ((commandArray[0] == "connect" || commandArray[0] == "telnet") &&
(commandArray[1] == "foodnstuff" || commandArray[1] == foodnstuffServ.ip)) {
Player.getCurrentServer().isConnectedTo = false;
Player.currentServer = foodnstuffServ.ip;
Player.getCurrentServer().isConnectedTo = true;
post("Connected to foodnstuff");
iTutorialNextStep();
} else {post("Wrong command! Try again!"); return;}
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.TerminalAnalyze:
if (commandArray[0] == "analyze") {
if (commandArray.length != 1) {
post("Incorrect usage of analyze command. Usage: analyze"); return;
}
//Analyze the current server for information
Terminal.analyzeFlag = true;
post("Analyzing system...");
hackProgressPost("Time left:");
hackProgressBarPost("[");
Player.analyze();
//Disable terminal
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
iTutorialNextStep();
} else {
post("Wrong command! Try again!");
}
break;
case iTutorialSteps.TerminalNuke:
if (commandArray.length == 2 &&
commandArray[0] == "run" && commandArray[1] == "NUKE.exe") {
foodnstuffServ.hasAdminRights = true;
post("NUKE successful! Gained root access to foodnstuff");
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.TerminalManualHack:
if (commandArray.length == 1 && commandArray[0] == "hack") {
Terminal.hackFlag = true;
hackProgressPost("Time left:");
hackProgressBarPost("[");
Player.hack();
//Disable terminal
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.TerminalCreateScript:
if (commandArray.length == 2 &&
commandArray[0] == "nano" && commandArray[1] == "foodnstuff.script") {
Engine.loadScriptEditorContent("foodnstuff", "");
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
case iTutorialSteps.TerminalFree:
if (commandArray.length == 1 && commandArray[0] == "free") {
Terminal.executeFreeCommand(commandArray);
iTutorialNextStep();
}
break;
case iTutorialSteps.TerminalRunScript:
if (commandArray.length == 2 &&
commandArray[0] == "run" && commandArray[1] == "foodnstuff.script") {
Terminal.runScript("foodnstuff.script");
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
case iTutorialSteps.ActiveScriptsToTerminal:
if (commandArray.length == 2 &&
commandArray[0] == "tail" && commandArray[1] == "foodnstuff.script") {
var currScripts = Player.getCurrentServer().scripts;
for (var i = 0; i < currScripts.length; ++i) {
if ("foodnstuff.script" == currScripts[i].filename) {
currScripts[i].displayLog();
}
}
iTutorialNextStep();
} else {post("Wrong command! Try again!");}
break;
default:
post("Please follow the tutorial, or click 'Exit Tutorial' if you'd like to skip it");
return;
}
return;
}
/****************** END INTERACTIVE TUTORIAL ******************/
/* Command parser */
switch (commandArray[0]) {
case "analyze":
if (commandArray.length != 1) {
@ -403,12 +525,7 @@ var Terminal = {
post("Host not found");
break;
case "free":
if (commandArray.length != 1) {
post("Incorrect usage of free command. Usage: free"); return;
}
post("Total: " + Player.getCurrentServer().maxRam.toString() + " GB");
post("Used: " + Player.getCurrentServer().ramUsed.toString() + " GB");
post("Available: " + (Player.getCurrentServer().maxRam - Player.getCurrentServer().ramUsed).toString() + " GB");
executeFreeCommand(commandArray);
break;
case "hack":
if (commandArray.length != 1) {
@ -479,27 +596,7 @@ var Terminal = {
post("No such script is running. Nothing to kill");
break;
case "ls":
if (commandArray.length != 1) {
post("Incorrect usage of ls command. Usage: ls"); return;
}
//Display all programs and scripts
var allFiles = [];
//Get all of the programs and scripts on the machine into one temporary array
for (var i = 0; i < Player.getCurrentServer().programs.length; i++) {
allFiles.push(Player.getCurrentServer().programs[i]);
}
for (var i = 0; i < Player.getCurrentServer().scripts.length; i++) {
allFiles.push(Player.getCurrentServer().scripts[i].filename);
}
//Sort the files alphabetically then print each
allFiles.sort();
for (var i = 0; i < allFiles.length; i++) {
post(allFiles[i]);
}
Terminal.executeListCommand(commandArray);
break;
case "nano":
if (commandArray.length != 2) {
@ -534,34 +631,7 @@ var Terminal = {
break;
case "netstat":
case "scan":
if (commandArray.length != 1) {
post("Incorrect usage of netstat/scan command. Usage: netstat/scan"); return;
}
//Displays available network connections using TCP
post("Hostname IP Root Access");
for (var i = 0; i < Player.getCurrentServer().serversOnNetwork.length; i++) {
//Add hostname
var entry = Player.getCurrentServer().getServerOnNetwork(i).hostname;
//Calculate padding and add IP
var numSpaces = 21 - entry.length;
var spaces = Array(numSpaces+1).join(" ");
entry += spaces;
entry += Player.getCurrentServer().getServerOnNetwork(i).ip;
//Calculate padding and add root access info
var hasRoot;
if (Player.getCurrentServer().getServerOnNetwork(i).hasAdminRights) {
hasRoot = 'Y';
} else {
hasRoot = 'N';
}
numSpaces = 21 - Player.getCurrentServer().getServerOnNetwork(i).ip.length;
spaces = Array(numSpaces+1).join(" ");
entry += spaces;
entry += hasRoot;
post(entry);
}
Terminal.executeScanCommand(commandArray);
break;
case "ps":
if (commandArray.length != 1) {
@ -620,6 +690,17 @@ var Terminal = {
case "scp":
//TODO
break;
case "sudov":
if (commandArray.length != 1) {
post("Incorrect number of arguments. Usage: sudov"); return;
}
if (Player.getCurrentServer().hasAdminRights) {
post("You have ROOT access to this machine");
} else {
post("You do NOT have root access to this machine");
}
break;
case "tail":
if (commandArray.length != 2) {
post("Incorrect number of arguments. Usage: tail [script]");
@ -651,6 +732,70 @@ var Terminal = {
post("Command not found");
}
},
executeListCommand: function(commandArray) {
if (commandArray.length != 1) {
post("Incorrect usage of ls command. Usage: ls"); return;
}
//Display all programs and scripts
var allFiles = [];
//Get all of the programs and scripts on the machine into one temporary array
for (var i = 0; i < Player.getCurrentServer().programs.length; i++) {
allFiles.push(Player.getCurrentServer().programs[i]);
}
for (var i = 0; i < Player.getCurrentServer().scripts.length; i++) {
allFiles.push(Player.getCurrentServer().scripts[i].filename);
}
//Sort the files alphabetically then print each
allFiles.sort();
for (var i = 0; i < allFiles.length; i++) {
post(allFiles[i]);
}
},
executeScanCommand: function(commandArray) {
if (commandArray.length != 1) {
post("Incorrect usage of netstat/scan command. Usage: netstat/scan"); return;
}
//Displays available network connections using TCP
post("Hostname IP Root Access");
for (var i = 0; i < Player.getCurrentServer().serversOnNetwork.length; i++) {
//Add hostname
var entry = Player.getCurrentServer().getServerOnNetwork(i).hostname;
//Calculate padding and add IP
var numSpaces = 21 - entry.length;
var spaces = Array(numSpaces+1).join(" ");
entry += spaces;
entry += Player.getCurrentServer().getServerOnNetwork(i).ip;
//Calculate padding and add root access info
var hasRoot;
if (Player.getCurrentServer().getServerOnNetwork(i).hasAdminRights) {
hasRoot = 'Y';
} else {
hasRoot = 'N';
}
numSpaces = 21 - Player.getCurrentServer().getServerOnNetwork(i).ip.length;
spaces = Array(numSpaces+1).join(" ");
entry += spaces;
entry += hasRoot;
post(entry);
}
},
executeFreeCommand: function(commandArray) {
if (commandArray.length != 1) {
post("Incorrect usage of free command. Usage: free"); return;
}
post("Total: " + Player.getCurrentServer().maxRam.toString() + " GB");
post("Used: " + Player.getCurrentServer().ramUsed.toString() + " GB");
post("Available: " + (Player.getCurrentServer().maxRam - Player.getCurrentServer().ramUsed).toString() + " GB");
},
//First called when the "run [program]" command is called. Checks to see if you
//have the executable and, if you do, calls the executeProgram() function

@ -675,7 +675,8 @@ var Engine = {
if (loadGame(saveObject)) {
console.log("Loaded game from save");
Engine.setDisplayElements(); //Sets variables for important DOM elements
Engine.init(); //Initialize main game loop, buttons, etc.
Engine.init(); //Initialize buttons, work, etc.
Engine.start(); //Run main game loop and Scripts loop
CompanyPositions.init();
//Calculate the number of cycles have elapsed while offline
@ -707,6 +708,7 @@ var Engine = {
//No save found, start new game
console.log("Initializing new game");
Engine.setDisplayElements(); //Sets variables for important DOM elements
Engine.start(); //Run main game loop and Scripts loop
SpecialServerIps = new SpecialServerIpsMap();
Player.init();
initForeignServers();
@ -715,7 +717,8 @@ var Engine = {
CompanyPositions.init();
initAugmentations();
//Start tutorial
//Start interactive tutorial
iTutorialStart();
}
},
@ -782,86 +785,6 @@ var Engine = {
//Script editor
Engine.Display.scriptEditorText = document.getElementById("script-editor-text");
}
/* Initialization */
init: function() {
//Main menu buttons and content
Engine.Clickables.terminalMainMenuButton = document.getElementById("terminal-menu-link");
Engine.Clickables.terminalMainMenuButton.addEventListener("click", function() {
Engine.loadTerminalContent();
return false;
});
Engine.Clickables.characterMainMenuButton = document.getElementById("character-menu-link");
Engine.Clickables.characterMainMenuButton.addEventListener("click", function() {
Engine.loadCharacterContent();
return false;
});
Engine.Clickables.scriptEditorMainMenuButton = document.getElementById("create-script-menu-link");
Engine.Clickables.scriptEditorMainMenuButton.addEventListener("click", function() {
Engine.loadScriptEditorContent();
return false;
});
Engine.Clickables.activeScriptsMainMenuButton = document.getElementById("active-scripts-menu-link");
Engine.Clickables.activeScriptsMainMenuButton.addEventListener("click", function() {
Engine.loadActiveScriptsContent();
return false;
});
Engine.Clickables.hacknetNodesMainMenuButton = document.getElementById("hacknet-nodes-menu-link");
Engine.Clickables.hacknetNodesMainMenuButton.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
return false;
});
Engine.Clickables.worldMainMenuButton = document.getElementById("world-menu-link");
Engine.Clickables.worldMainMenuButton.addEventListener("click", function() {
Engine.loadWorldContent();
return false;
});
Engine.Clickables.createProgramMainMenuButton = document.getElementById("create-program-menu-link");
Engine.Clickables.createProgramMainMenuButton.addEventListener("click", function() {
Engine.loadCreateProgramContent();
return false;
});
Engine.Clickables.factionsMainMenuButton = document.getElementById("factions-menu-link");
Engine.Clickables.factionsMainMenuButton.addEventListener("click", function() {
Engine.loadFactionsContent();
return false;
});
Engine.Clickables.augmentationsMainMenuButton = document.getElementById("augmentations-menu-link");
Engine.Clickables.augmentationsMainMenuButton.addEventListener("click", function() {
Engine.loadAugmentationsContent();
return false;
});
Engine.Clickables.tutorialMainMenuButton = document.getElementById("tutorial-menu-link");
Engine.Clickables.tutorialMainMenuButton.addEventListener("click", function() {
Engine.loadTutorialContent();
return false;
});
//Active scripts list
Engine.ActiveScriptsList = document.getElementById("active-scripts-list");
//Save and Delete buttons
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
Engine.Clickables.saveMainMenuButton.addEventListener("click", function() {
saveObject.saveGame();
return false;
});
Engine.Clickables.deleteMainMenuButton = document.getElementById("delete-game-link");
Engine.Clickables.deleteMainMenuButton.addEventListener("click", function() {
saveObject.deleteGame();
return false;
});
//Tutorial buttons
Engine.Clickables.tutorialGettingStartedButton = document.getElementById("tutorial-getting-started-link");
@ -908,6 +831,96 @@ var Engine = {
Engine.Clickables.tutorialBackButton.addEventListener("click", function() {
Engine.displayTutorialContent();
});
},
/* Initialization */
init: function() {
//Main menu buttons and content
Engine.Clickables.terminalMainMenuButton = clearEventListeners("terminal-menu-link");
//Engine.Clickables.terminalMainMenuButton = document.getElementById("terminal-menu-link");
Engine.Clickables.terminalMainMenuButton.addEventListener("click", function() {
Engine.loadTerminalContent();
return false;
});
Engine.Clickables.characterMainMenuButton = clearEventListeners("character-menu-link");
//Engine.Clickables.characterMainMenuButton = document.getElementById("character-menu-link");
Engine.Clickables.characterMainMenuButton.addEventListener("click", function() {
Engine.loadCharacterContent();
return false;
});
Engine.Clickables.scriptEditorMainMenuButton = clearEventListeners("create-script-menu-link");
//Engine.Clickables.scriptEditorMainMenuButton = document.getElementById("create-script-menu-link");
Engine.Clickables.scriptEditorMainMenuButton.addEventListener("click", function() {
Engine.loadScriptEditorContent();
return false;
});
//Engine.Clickables.activeScriptsMainMenuButton = document.getElementById("active-scripts-menu-link");
Engine.Clickables.activeScriptsMainMenuButton = clearEventListeners("active-scripts-menu-link");
Engine.Clickables.activeScriptsMainMenuButton.addEventListener("click", function() {
Engine.loadActiveScriptsContent();
return false;
});
Engine.Clickables.hacknetNodesMainMenuButton = clearEventListeners("hacknet-nodes-menu-link");
//Engine.Clickables.hacknetNodesMainMenuButton = document.getElementById("hacknet-nodes-menu-link");
Engine.Clickables.hacknetNodesMainMenuButton.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
return false;
});
Engine.Clickables.worldMainMenuButton = clearEventListeners("world-menu-link");
//Engine.Clickables.worldMainMenuButton = document.getElementById("world-menu-link");
Engine.Clickables.worldMainMenuButton.addEventListener("click", function() {
Engine.loadWorldContent();
return false;
});
Engine.Clickables.createProgramMainMenuButton = clearEventListeners("create-program-menu-link");
//Engine.Clickables.createProgramMainMenuButton = document.getElementById("create-program-menu-link");
Engine.Clickables.createProgramMainMenuButton.addEventListener("click", function() {
Engine.loadCreateProgramContent();
return false;
});
Engine.Clickables.factionsMainMenuButton = clearEventListeners("factions-menu-link");
//Engine.Clickables.factionsMainMenuButton = document.getElementById("factions-menu-link");
Engine.Clickables.factionsMainMenuButton.addEventListener("click", function() {
Engine.loadFactionsContent();
return false;
});
Engine.Clickables.augmentationsMainMenuButton = clearEventListeners("augmentations-menu-link");
//Engine.Clickables.augmentationsMainMenuButton = document.getElementById("augmentations-menu-link");
Engine.Clickables.augmentationsMainMenuButton.addEventListener("click", function() {
Engine.loadAugmentationsContent();
return false;
});
Engine.Clickables.tutorialMainMenuButton = clearEventListeners("tutorial-menu-link");
//Engine.Clickables.tutorialMainMenuButton = document.getElementById("tutorial-menu-link");
Engine.Clickables.tutorialMainMenuButton.addEventListener("click", function() {
Engine.loadTutorialContent();
return false;
});
//Active scripts list
Engine.ActiveScriptsList = document.getElementById("active-scripts-list");
//Save and Delete buttons
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
Engine.Clickables.saveMainMenuButton.addEventListener("click", function() {
saveObject.saveGame();
return false;
});
Engine.Clickables.deleteMainMenuButton = document.getElementById("delete-game-link");
Engine.Clickables.deleteMainMenuButton.addEventListener("click", function() {
saveObject.deleteGame();
return false;
});
//Create Program buttons
var portHackALink = document.getElementById("create-program-porthack");
@ -934,9 +947,6 @@ var Engine = {
sqlInjectALink.addEventListener("click", function() {
Player.startCreateProgramWork(Programs.SQLInjectProgram, CONSTANTS.MillisecondsPer8Hours);
});
//Message at the top of terminal
postNetburnerText();
@ -958,8 +968,9 @@ var Engine = {
});
Engine.loadWorkInProgressContent();
}
},
start: function() {
//Run main loop
Engine.idleTimer();

@ -13,11 +13,11 @@ function sizeOfObject(obj) {
//e.g. addOffset(100, 5) will return anything from 95 to 105.
//The percentage argument must be between 0 and 100;
function addOffset(n, percentage) {
if (percentage < 0 || percentage > 100) {return ;}
if (percentage < 0 || percentage > 100) {return;}
var offset = n * (percentage / 100);
return n * (Math.random() * (2 * offset) - offset);
return n + ((Math.random() * (2 * offset)) - offset);
}
//Given an element by its Id(usually an 'a' element), removes all event listeners
@ -27,5 +27,5 @@ function clearEventListeners(elemId) {
if (elem == null) {console.log("ERR: Could not find element for: " + elemId); return null;}
var newElem = elem.cloneNode(true);
elem.parentNode.replaceChild(newElem, elem);
return elem;
return newElem;
}