mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-10 09:43:54 +01:00
ITutorial in react
This commit is contained in:
parent
61e3959a25
commit
023f2b8309
4
src/InteractiveTutorial.d.ts
vendored
4
src/InteractiveTutorial.d.ts
vendored
@ -1,3 +1,5 @@
|
||||
export declare function iTutorialNextStep(): void;
|
||||
export declare function iTutorialPrevStep(): void;
|
||||
export declare function iTutorialEnd(): void;
|
||||
export declare const ITutorial: { isRunning: boolean; currStep: number };
|
||||
export declare const iTutorialSteps: { [key: string]: number };
|
||||
export declare const iTutorialSteps: { [key: string]: number | undefined };
|
||||
|
@ -4,6 +4,8 @@ import { Settings } from "./Settings/Settings";
|
||||
|
||||
import { LiteratureNames } from "./Literature/data/LiteratureNames";
|
||||
|
||||
import { ITutorialEvents } from "./ui/InteractiveTutorial/ITutorialEvents";
|
||||
|
||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||
@ -67,328 +69,6 @@ function iTutorialStart() {
|
||||
Engine.Counters.autoSaveCounter = Infinity;
|
||||
ITutorial.currStep = 0;
|
||||
ITutorial.isRunning = true;
|
||||
|
||||
document.getElementById("interactive-tutorial-container").style.display = "block";
|
||||
|
||||
// Exit tutorial button
|
||||
const exitButton = clearEventListeners("interactive-tutorial-exit");
|
||||
exitButton.addEventListener("click", function () {
|
||||
iTutorialEnd();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Back button
|
||||
const backButton = clearEventListeners("interactive-tutorial-back");
|
||||
backButton.addEventListener("click", function () {
|
||||
iTutorialPrevStep();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Next button
|
||||
const nextButton = clearEventListeners("interactive-tutorial-next");
|
||||
nextButton.addEventListener("click", function () {
|
||||
iTutorialNextStep();
|
||||
return false;
|
||||
});
|
||||
|
||||
iTutorialEvaluateStep();
|
||||
}
|
||||
|
||||
function iTutorialEvaluateStep() {
|
||||
if (!ITutorial.isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable and clear main menu
|
||||
// const terminalMainMenu = clearEventListeners("terminal-menu-link");
|
||||
// const statsMainMenu = clearEventListeners("stats-menu-link");
|
||||
// const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
|
||||
// const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
|
||||
// const cityMainMenu = clearEventListeners("city-menu-link");
|
||||
// const tutorialMainMenu = clearEventListeners("tutorial-menu-link");
|
||||
// terminalMainMenu.removeAttribute("class");
|
||||
// statsMainMenu.removeAttribute("class");
|
||||
// activeScriptsMainMenu.removeAttribute("class");
|
||||
// hacknetMainMenu.removeAttribute("class");
|
||||
// cityMainMenu.removeAttribute("class");
|
||||
// tutorialMainMenu.removeAttribute("class");
|
||||
|
||||
// Interactive Tutorial Next button
|
||||
const nextBtn = document.getElementById("interactive-tutorial-next");
|
||||
|
||||
switch (ITutorial.currStep) {
|
||||
case iTutorialSteps.Start:
|
||||
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. " +
|
||||
"You may skip the tutorial at any time.",
|
||||
);
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.GoToCharacterPage:
|
||||
iTutorialSetText(
|
||||
"Let's start by heading to the Stats page. Click the <code class='interactive-tutorial-tab flashing-button'>Stats</code> tab on " +
|
||||
"the main navigation menu (left-hand side of the screen)",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
break;
|
||||
case iTutorialSteps.CharacterPage:
|
||||
iTutorialSetText(
|
||||
"The <code class='interactive-tutorial-tab'>Stats</code> page shows a lot of important information about your progress, " +
|
||||
"such as your skills, money, and bonuses. ",
|
||||
);
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.CharacterGoToTerminalPage:
|
||||
iTutorialSetText(
|
||||
"Let's head to your computer's terminal by clicking the <code class='interactive-tutorial-tab flashing-button'>Terminal</code> tab on the " +
|
||||
"main navigation menu.",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
break;
|
||||
case iTutorialSteps.TerminalIntro:
|
||||
iTutorialSetText(
|
||||
"The <code class='interactive-tutorial-tab'>Terminal</code> is used to interface with your home computer as well as " +
|
||||
"all of the other machines around the world.",
|
||||
);
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.TerminalHelp:
|
||||
iTutorialSetText(
|
||||
"Let's try it out. Start by entering the <code class='interactive-tutorial-command'>help</code> command into the <code class='interactive-tutorial-tab'>Terminal</code> " +
|
||||
"(Don't forget to press Enter after typing the command)",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalLs:
|
||||
iTutorialSetText(
|
||||
"The <code class='interactive-tutorial-command'>help</code> command displays a list of all available <code class='interactive-tutorial-tab'>Terminal</code> commands, how to use them, " +
|
||||
"and a description of what they do. <br><br>Let's try another command. Enter the <code class='interactive-tutorial-command'>ls</code> command.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalScan:
|
||||
iTutorialSetText(
|
||||
" <code class='interactive-tutorial-command'>ls</code> is a basic command that shows files " +
|
||||
"on the computer. Right now, it shows that you have a program called <code class='interactive-tutorial-command'>NUKE.exe</code> on your computer. " +
|
||||
"We'll get to what this does later. <br><br>Using your home computer's terminal, you can connect " +
|
||||
"to other machines throughout the world. Let's do that now by first entering " +
|
||||
"the <code class='interactive-tutorial-command'>scan</code> command.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalScanAnalyze1:
|
||||
iTutorialSetText(
|
||||
"The <code class='interactive-tutorial-command'>scan</code> command shows all available network connections. In other words, " +
|
||||
"it displays a list of all servers that can be connected to from your " +
|
||||
"current machine. A server is identified by its hostname. <br><br> " +
|
||||
"That's great and all, but there's so many servers. Which one should you go to? " +
|
||||
"The <code class='interactive-tutorial-command'>scan-analyze</code> command gives some more detailed information about servers on the " +
|
||||
"network. Try it now!",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalScanAnalyze2:
|
||||
iTutorialSetText(
|
||||
"You just ran <code class='interactive-tutorial-command'>scan-analyze</code> 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). <br><br> It is also possible to run <code class='interactive-tutorial-command'>scan-analyze</code> with " +
|
||||
"a higher depth. Let's try a depth of two with the following command: <code class='interactive-tutorial-command'>scan-analyze 2</code>.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalConnect:
|
||||
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 <code class='interactive-tutorial-command'>connect [hostname]</code> command.<br><br>" +
|
||||
"From the results of the <code class='interactive-tutorial-command'>scan-analyze</code> command, we can see that the <code class='interactive-tutorial-command'>n00dles</code> server is " +
|
||||
"only one node away. Let's connect so it now using: <code class='interactive-tutorial-command'>connect n00dles</code>",
|
||||
);
|
||||
nextBtn.style.display = "none"; // 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 <code class='interactive-tutorial-command'>analyze</code> command.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalNuke:
|
||||
iTutorialSetText(
|
||||
"When the <code class='interactive-tutorial-command'>analyze</code> command finishes running it will show useful information " +
|
||||
"about hacking the server. <br><br> For this server, the required hacking skill is only <span class='character-hack-cell'>1</span>, " +
|
||||
"which means you can hack it right now. However, in order to hack a server " +
|
||||
"you must first gain root access. The <code class='interactive-tutorial-command'>NUKE.exe</code> 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 <code class='interactive-tutorial-command'>analyze</code> 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 " +
|
||||
"<code class='interactive-tutorial-command'>run NUKE.exe</code> command.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalManualHack:
|
||||
iTutorialSetText(
|
||||
"You now have root access! You can hack the server using the <code class='interactive-tutorial-command'>hack</code> command. " +
|
||||
"Try doing that now.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalHackingMechanics:
|
||||
iTutorialSetText(
|
||||
"You are now attempting to hack the server. Performing a hack takes time and " +
|
||||
"only has a certain percentage chance " +
|
||||
"of success. This time and success chance is determined by a variety of factors, including " +
|
||||
"your hacking skill and the server's security level.<br><br>" +
|
||||
"If your attempt to hack the server is successful, you will steal a certain percentage " +
|
||||
"of the server's total money. This percentage is affected by your hacking skill and " +
|
||||
"the server's security level.<br><br>The amount of money on a server is not limitless. So, if " +
|
||||
"you constantly hack a server and deplete its money, then you will encounter " +
|
||||
"diminishing returns in your hacking.",
|
||||
);
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.TerminalCreateScript:
|
||||
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!<br><br>To create a new script or edit an existing one, you can use the <code class='interactive-tutorial-command'>nano</code> " +
|
||||
"command. Scripts must end with the <code class='interactive-tutorial-command'>.script</code> extension. Let's make a script now by " +
|
||||
"entering <code class='interactive-tutorial-command'>nano n00dles.script</code> after the hack command finishes running (Sidenote: Pressing ctrl + c" +
|
||||
" will end a command like hack early)",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalTypeScript:
|
||||
iTutorialSetText(
|
||||
"This is the script editor. You can use it to program your scripts. Scripts are " +
|
||||
"written in a simplified version of javascript. Copy and paste the following code into the script editor: <br><br>" +
|
||||
"<pre class='interactive-tutorial-code'>" +
|
||||
"while(true) {\n" +
|
||||
" hack('n00dles');\n" +
|
||||
"}</pre>" +
|
||||
"For anyone with basic programming experience, this code should be straightforward. " +
|
||||
"This script will continuously hack the <code class='interactive-tutorial-command'>n00dles</code> server.<br><br>" +
|
||||
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js)
|
||||
break;
|
||||
case iTutorialSteps.TerminalFree:
|
||||
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 <code class='interactive-tutorial-command'>free</code> command.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal commmand
|
||||
break;
|
||||
case iTutorialSteps.TerminalRunScript:
|
||||
iTutorialSetText(
|
||||
"We have 4GB of free RAM on this machine, which is enough to run our " +
|
||||
"script. Let's run our script using <code class='interactive-tutorial-command'>run n00dles.script</code>.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal commmand
|
||||
break;
|
||||
case iTutorialSteps.TerminalGoToActiveScriptsPage:
|
||||
iTutorialSetText(
|
||||
"Your script is now running! " +
|
||||
"It will continuously run in the background and will automatically stop if " +
|
||||
"the code ever completes (the <code class='interactive-tutorial-command'>n00dles.script</code> will never complete because it " +
|
||||
"runs an infinite loop). <br><br>These scripts can passively earn you income and hacking experience. " +
|
||||
"Your scripts will also earn money and experience while you are offline, although at a " +
|
||||
"slightly slower rate. <br><br> " +
|
||||
"Let's check out some statistics for our running scripts by clicking the " +
|
||||
"<code class='interactive-tutorial-tab flashing-button'>Active Scripts</code> link in the main navigation menu.",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
break;
|
||||
case iTutorialSteps.ActiveScriptsPage:
|
||||
iTutorialSetText(
|
||||
"This page displays information about all of your scripts that are " +
|
||||
"running across every server. You can use this to gauge how well " +
|
||||
"your scripts are doing. Let's go back to the <code class='interactive-tutorial-tab flashing-button'>Terminal</code>",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
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 <code class='interactive-tutorial-command'>tail</code> command. Do that " +
|
||||
"now for the script we just ran by typing <code class='interactive-tutorial-command'>tail n00dles.script</code>",
|
||||
);
|
||||
nextBtn.style.display = "none"; // 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 covers the basics of hacking. To learn more about writing " +
|
||||
"scripts, select the <code class='interactive-tutorial-tab'>Tutorial</code> link in the " +
|
||||
"main navigation menu to look at the documentation. " +
|
||||
"<strong style='background-color:#444;'>If you are an experienced JavaScript " +
|
||||
"developer, I would highly suggest you check out the section on " +
|
||||
"NetscriptJS/Netscript 2.0, it's faster and more powerful.</strong><br><br>For now, let's move on to something else!",
|
||||
);
|
||||
nextBtn.style.display = "inline-block";
|
||||
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 <code class='interactive-tutorial-tab flashing-button'>Hacknet</code> page through the main navigation menu now.",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
break;
|
||||
case iTutorialSteps.HacknetNodesIntroduction:
|
||||
iTutorialSetText(
|
||||
"here you can purchase new Hacknet Nodes and upgrade your " + "existing ones. Let's purchase a new one now.",
|
||||
);
|
||||
nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js)
|
||||
break;
|
||||
case iTutorialSteps.HacknetNodesGoToWorldPage:
|
||||
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.<br><br>" +
|
||||
"Let's go to the <code class='interactive-tutorial-tab flashing-button'>City</code> page through the main navigation menu.",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
break;
|
||||
case iTutorialSteps.WorldDescription:
|
||||
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!<br><br>" +
|
||||
"Lastly, click on the <code class='interactive-tutorial-tab flashing-button'>Tutorial</code> link in the main navigation menu.",
|
||||
);
|
||||
nextBtn.style.display = "none";
|
||||
break;
|
||||
case iTutorialSteps.TutorialPageInfo:
|
||||
iTutorialSetText(
|
||||
"This page contains a lot of different documentation about the game's " +
|
||||
"content and mechanics. <strong style='background-color:#444;'> I know it's a lot, but I highly suggest you read " +
|
||||
"(or at least skim) through this before you start playing</strong>. That's the end of the tutorial. " +
|
||||
"Hope you enjoy the game!",
|
||||
);
|
||||
nextBtn.style.display = "inline-block";
|
||||
nextBtn.innerHTML = "Finish Tutorial";
|
||||
break;
|
||||
case iTutorialSteps.End:
|
||||
iTutorialEnd();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid tutorial step");
|
||||
}
|
||||
|
||||
if (ITutorial.stepIsDone[ITutorial.currStep] === true) {
|
||||
nextBtn.style.display = "inline-block";
|
||||
}
|
||||
}
|
||||
|
||||
// Go to the next step and evaluate it
|
||||
@ -397,7 +77,8 @@ function iTutorialNextStep() {
|
||||
if (ITutorial.currStep < iTutorialSteps.End) {
|
||||
ITutorial.currStep += 1;
|
||||
}
|
||||
iTutorialEvaluateStep();
|
||||
if (ITutorial.currStep === iTutorialSteps.End) iTutorialEnd();
|
||||
ITutorialEvents.emit();
|
||||
}
|
||||
|
||||
// Go to previous step and evaluate
|
||||
@ -405,22 +86,11 @@ function iTutorialPrevStep() {
|
||||
if (ITutorial.currStep > iTutorialSteps.Start) {
|
||||
ITutorial.currStep -= 1;
|
||||
}
|
||||
iTutorialEvaluateStep();
|
||||
ITutorialEvents.emit();
|
||||
}
|
||||
|
||||
function iTutorialEnd() {
|
||||
// Re-enable auto save
|
||||
if (Settings.AutosaveInterval === 0) {
|
||||
Engine.Counters.autoSaveCounter = Infinity;
|
||||
} else {
|
||||
Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
|
||||
}
|
||||
|
||||
Engine.init();
|
||||
|
||||
ITutorial.currStep = iTutorialSteps.End;
|
||||
ITutorial.isRunning = false;
|
||||
document.getElementById("interactive-tutorial-container").style.display = "none";
|
||||
|
||||
// Create a popup with final introductory stuff
|
||||
const popupId = "interactive-tutorial-ending-popup";
|
||||
@ -445,20 +115,7 @@ function iTutorialEnd() {
|
||||
createPopup(popupId, [txt, gotitBtn]);
|
||||
|
||||
Player.getHomeComputer().messages.push(LiteratureNames.HackersStartingHandbook);
|
||||
ITutorialEvents.emit();
|
||||
}
|
||||
|
||||
let textBox = null;
|
||||
(function () {
|
||||
function set() {
|
||||
textBox = document.getElementById("interactive-tutorial-text");
|
||||
document.removeEventListener("DOMContentLoaded", set);
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", set);
|
||||
})();
|
||||
|
||||
function iTutorialSetText(txt) {
|
||||
textBox.innerHTML = txt;
|
||||
textBox.parentElement.scrollTop = 0; // this resets scroll position
|
||||
}
|
||||
|
||||
export { iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, ITutorial };
|
||||
export { iTutorialSteps, iTutorialEnd, iTutorialStart, iTutorialNextStep, ITutorial, iTutorialPrevStep };
|
||||
|
@ -103,6 +103,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
}
|
||||
lastPosition = null;
|
||||
|
||||
// this is duplicate code with saving later.
|
||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||
//Make sure filename + code properly follow tutorial
|
||||
if (filename !== "n00dles.script") {
|
||||
@ -117,20 +118,24 @@ export function Root(props: IProps): React.ReactElement {
|
||||
//Save the script
|
||||
const server = props.player.getCurrentServer();
|
||||
if (server === null) throw new Error("Server should not be null but it is.");
|
||||
let found = false;
|
||||
for (let i = 0; i < server.scripts.length; i++) {
|
||||
if (filename == server.scripts[i].filename) {
|
||||
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
|
||||
props.router.toTerminal();
|
||||
return iTutorialNextStep();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current script does NOT exist, create a new one
|
||||
const script = new Script();
|
||||
script.saveScript(code, props.player.currentServer, server.scripts);
|
||||
server.scripts.push(script);
|
||||
if (!found) {
|
||||
const script = new Script();
|
||||
script.saveScript(code, props.player.currentServer, server.scripts);
|
||||
server.scripts.push(script);
|
||||
}
|
||||
|
||||
return iTutorialNextStep();
|
||||
iTutorialNextStep();
|
||||
|
||||
props.router.toTerminal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (filename == "") {
|
||||
|
@ -88,8 +88,8 @@ export class Terminal implements ITerminal {
|
||||
process(router: IRouter, player: IPlayer, cycles: number): void {
|
||||
if (this.action === null) return;
|
||||
this.action.timeLeft -= (CONSTANTS._idleSpeed * cycles) / 1000;
|
||||
TerminalEvents.emit();
|
||||
if (this.action.timeLeft < 0) this.finishAction(router, player, false);
|
||||
TerminalEvents.emit();
|
||||
}
|
||||
|
||||
append(item: Output | Link): void {
|
||||
@ -97,16 +97,15 @@ export class Terminal implements ITerminal {
|
||||
if (this.outputHistory.length > Settings.MaxTerminalCapacity) {
|
||||
this.outputHistory.slice(this.outputHistory.length - Settings.MaxTerminalCapacity);
|
||||
}
|
||||
TerminalEvents.emit();
|
||||
}
|
||||
|
||||
print(s: string): void {
|
||||
this.append(new Output(s, "primary"));
|
||||
TerminalEvents.emit();
|
||||
}
|
||||
|
||||
error(s: string): void {
|
||||
this.append(new Output(s, "error"));
|
||||
TerminalEvents.emit();
|
||||
}
|
||||
|
||||
startHack(player: IPlayer): void {
|
||||
@ -246,6 +245,7 @@ export class Terminal implements ITerminal {
|
||||
this.print("Cancelled");
|
||||
}
|
||||
this.action = null;
|
||||
TerminalEvents.emit();
|
||||
}
|
||||
|
||||
getFile(player: IPlayer, filename: string): Script | TextFile | string | null {
|
||||
@ -515,51 +515,47 @@ export class Terminal implements ITerminal {
|
||||
switch (ITutorial.currStep) {
|
||||
case iTutorialSteps.TerminalHelp:
|
||||
if (commandArray.length === 1 && commandArray[0] == "help") {
|
||||
TerminalHelpText.forEach((line) => this.print(line));
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalLs:
|
||||
if (commandArray.length === 1 && commandArray[0] == "ls") {
|
||||
ls(this, router, player, s, commandArray.slice(1));
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalScan:
|
||||
if (commandArray.length === 1 && commandArray[0] == "scan") {
|
||||
scan(this, router, player, s, commandArray.slice(1));
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalScanAnalyze1:
|
||||
if (commandArray.length == 1 && commandArray[0] == "scan-analyze") {
|
||||
this.executeScanAnalyzeCommand(player, 1);
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalScanAnalyze2:
|
||||
if (commandArray.length == 2 && commandArray[0] == "scan-analyze" && commandArray[1] === 2) {
|
||||
this.executeScanAnalyzeCommand(player, 2);
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalConnect:
|
||||
if (commandArray.length == 2) {
|
||||
if (commandArray[0] == "connect" && (commandArray[1] == "n00dles" || commandArray[1] == n00dlesServ.ip)) {
|
||||
player.getCurrentServer().isConnectedTo = false;
|
||||
player.currentServer = n00dlesServ.ip;
|
||||
player.getCurrentServer().isConnectedTo = true;
|
||||
this.print("Connected to n00dles");
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Wrong command! Try again!");
|
||||
@ -567,80 +563,69 @@ export class Terminal implements ITerminal {
|
||||
}
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalAnalyze:
|
||||
if (commandArray.length === 1 && commandArray[0] === "analyze") {
|
||||
if (commandArray.length !== 1) {
|
||||
this.print("Incorrect usage of analyze command. Usage: analyze");
|
||||
return;
|
||||
}
|
||||
this.startAnalyze();
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalNuke:
|
||||
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "NUKE.exe") {
|
||||
n00dlesServ.hasAdminRights = true;
|
||||
this.print("NUKE successful! Gained root access to n00dles");
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalManualHack:
|
||||
if (commandArray.length == 1 && commandArray[0] == "hack") {
|
||||
this.startHack(player);
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalCreateScript:
|
||||
if (commandArray.length == 2 && commandArray[0] == "nano" && commandArray[1] == "n00dles.script") {
|
||||
router.toScriptEditor("n00dles.script", "");
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalFree:
|
||||
if (commandArray.length == 1 && commandArray[0] == "free") {
|
||||
free(this, router, player, s, commandArray.slice(1));
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.TerminalRunScript:
|
||||
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "n00dles.script") {
|
||||
run(this, router, player, s, commandArray.slice(1));
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case iTutorialSteps.ActiveScriptsToTerminal:
|
||||
if (commandArray.length == 2 && commandArray[0] == "tail" && commandArray[1] == "n00dles.script") {
|
||||
// Check that the script exists on this machine
|
||||
const runningScript = findRunningScript("n00dles.script", [], player.getCurrentServer());
|
||||
if (runningScript == null) {
|
||||
this.print("Error: No such script exists");
|
||||
return;
|
||||
}
|
||||
logBoxCreate(runningScript);
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.print("Please follow the tutorial, or click 'Exit Tutorial' if you'd like to skip it");
|
||||
this.print("Please follow the tutorial, or click 'EXIT' if you'd like to skip it");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/****************** END INTERACTIVE TUTORIAL ******************/
|
||||
/* Command parser */
|
||||
|
@ -50,9 +50,9 @@ interface IProps {
|
||||
|
||||
export function TerminalRoot({ terminal, router, player }: IProps): React.ReactElement {
|
||||
const scrollHook = useRef<HTMLDivElement>(null);
|
||||
const setRerender = useState(false)[1];
|
||||
const setRerender = useState(0)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
setRerender((old) => old + 1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -9,6 +9,9 @@ import { onExport } from "../ExportBonus";
|
||||
import { LocationName } from "../Locations/data/LocationNames";
|
||||
import { Location } from "../Locations/Location";
|
||||
import { Locations } from "../Locations/Locations";
|
||||
import { ITutorial } from "../InteractiveTutorial";
|
||||
import { InteractiveTutorialRoot } from "./InteractiveTutorial/InteractiveTutorialRoot";
|
||||
import { ITutorialEvents } from "./InteractiveTutorial/ITutorialEvents";
|
||||
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { prestigeAugmentation } from "../Prestige";
|
||||
@ -31,6 +34,7 @@ import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
import { Page, IRouter } from "./Router";
|
||||
import { Overview } from "./React/Overview";
|
||||
import { SidebarRoot } from "../Sidebar/ui/SidebarRoot";
|
||||
import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot";
|
||||
import { DevMenuRoot } from "../DevMenu";
|
||||
@ -179,6 +183,7 @@ function determineStartPage(player: IPlayer): Page {
|
||||
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const [page, setPage] = useState(determineStartPage(player));
|
||||
const setRerender = useState(0)[1];
|
||||
const [faction, setFaction] = useState<Faction>(
|
||||
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
|
||||
);
|
||||
@ -193,6 +198,13 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
|
||||
const [cinematicText, setCinematicText] = useState("");
|
||||
|
||||
function rerender(): void {
|
||||
setRerender((old) => old + 1);
|
||||
}
|
||||
useEffect(() => {
|
||||
return ITutorialEvents.subscribe(rerender);
|
||||
}, []);
|
||||
|
||||
Router = {
|
||||
page: () => page,
|
||||
toActiveScripts: () => setPage(Page.ActiveScripts),
|
||||
@ -255,13 +267,19 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
useEffect(() => {
|
||||
filename = "";
|
||||
code = "";
|
||||
window.scrollTo(0, 0);
|
||||
if (page !== Page.Terminal) window.scrollTo(0, 0);
|
||||
});
|
||||
|
||||
return (
|
||||
<Context.Player.Provider value={player}>
|
||||
<Context.Router.Provider value={Router}>
|
||||
<CharacterOverview save={() => saveObject.saveGame(engine.indexedDb)} />
|
||||
<Overview>
|
||||
{!ITutorial.isRunning ? (
|
||||
<CharacterOverview save={() => saveObject.saveGame(engine.indexedDb)} />
|
||||
) : (
|
||||
<InteractiveTutorialRoot />
|
||||
)}
|
||||
</Overview>
|
||||
{page === Page.BitVerse ? (
|
||||
<BitverseRoot flume={flume} enter={enterBitNode} quick={quick} />
|
||||
) : page === Page.Infiltration ? (
|
||||
|
2
src/ui/InteractiveTutorial/ITutorialEvents.ts
Normal file
2
src/ui/InteractiveTutorial/ITutorialEvents.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import { EventEmitter } from "../../utils/EventEmitter";
|
||||
export const ITutorialEvents = new EventEmitter<[]>();
|
493
src/ui/InteractiveTutorial/InteractiveTutorialRoot.tsx
Normal file
493
src/ui/InteractiveTutorial/InteractiveTutorialRoot.tsx
Normal file
@ -0,0 +1,493 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Button from "@mui/material/Button";
|
||||
import ArrowForwardIos from "@mui/icons-material/ArrowForwardIos";
|
||||
import ArrowBackIos from "@mui/icons-material/ArrowBackIos";
|
||||
import { ITutorialEvents } from "./ITutorialEvents";
|
||||
import { use } from "../Context";
|
||||
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
||||
import LastPageIcon from "@mui/icons-material/LastPage";
|
||||
import HelpIcon from "@mui/icons-material/Help";
|
||||
import AccountTreeIcon from "@mui/icons-material/AccountTree";
|
||||
import StorageIcon from "@mui/icons-material/Storage";
|
||||
import LocationCityIcon from "@mui/icons-material/LocationCity";
|
||||
|
||||
import {
|
||||
iTutorialPrevStep,
|
||||
iTutorialNextStep,
|
||||
ITutorial,
|
||||
iTutorialSteps,
|
||||
iTutorialEnd,
|
||||
} from "../../InteractiveTutorial";
|
||||
|
||||
interface IContent {
|
||||
content: React.ReactElement;
|
||||
canNext: boolean;
|
||||
}
|
||||
|
||||
const contents: { [number: string]: IContent | undefined } = {
|
||||
[iTutorialSteps.Start as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
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. You may skip the tutorial at any time.
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.GoToCharacterPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>Let's start by heading to the Stats page. Click</Typography>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"error"} />
|
||||
<Typography color={"error"}>Stats</Typography>
|
||||
</ListItem>
|
||||
|
||||
<Typography>on the main navigation menu (left-hand side of the screen)</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.CharacterPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"primary"} />
|
||||
<Typography color={"primary"}>Stats</Typography>
|
||||
</ListItem>
|
||||
<Typography>
|
||||
shows a lot of important information about your progress, such as your skills, money, and bonuses.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.CharacterGoToTerminalPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>Let's head to your computer's terminal by clicking</Typography>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"error"} />
|
||||
<Typography color={"error"}>Terminal</Typography>
|
||||
</ListItem>
|
||||
<Typography>on the main navigation menu.</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalIntro as number]: {
|
||||
content: (
|
||||
<>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"primary"} />
|
||||
<Typography color={"primary"}>Terminal</Typography>
|
||||
</ListItem>
|
||||
<Typography>
|
||||
is used to interface with your home computer as well as all of the other machines around the world.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.TerminalHelp as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
Let's try it out. Start by entering the <code className="interactive-tutorial-command">help</code> command
|
||||
into the
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"primary"} />
|
||||
<Typography color={"primary"}>Terminal</Typography>
|
||||
</ListItem>
|
||||
<Typography>(Don't forget to press Enter after typing the command)</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalLs as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
The <code className="interactive-tutorial-command">help</code> command displays a list of all available
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"primary"} />
|
||||
<Typography color={"primary"}>Terminal</Typography>
|
||||
</ListItem>
|
||||
<Typography>
|
||||
commands, how to use them, and a description of what they do. <br />
|
||||
<br />
|
||||
Let's try another command. Enter the <code className="interactive-tutorial-command">ls</code> command.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalScan as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
<code className="interactive-tutorial-command">ls</code> is a basic command that shows files on the computer.
|
||||
Right now, it shows that you have a program called{" "}
|
||||
<code className="interactive-tutorial-command">NUKE.exe</code> on your computer. We'll get to what this does
|
||||
later. <br />
|
||||
<br />
|
||||
Using your home computer's terminal, you can connect to other machines throughout the world. Let's do that now
|
||||
by first entering the <code className="interactive-tutorial-command">scan</code> command.
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalScanAnalyze1 as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
The <code className="interactive-tutorial-command">scan</code> command shows all available network connections.
|
||||
In other words, it displays a list of all servers that can be connected to from your current machine. A server
|
||||
is identified by its hostname. <br />
|
||||
<br />
|
||||
That's great and all, but there's so many servers. Which one should you go to? The{" "}
|
||||
<code className="interactive-tutorial-command">scan-analyze</code> command gives some more detailed information
|
||||
about servers on the network. Try it now!
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalScanAnalyze2 as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
You just ran <code className="interactive-tutorial-command">scan-analyze</code> 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). <br />
|
||||
<br /> It is also possible to run <code className="interactive-tutorial-command">scan-analyze</code> with a
|
||||
higher depth. Let's try a depth of two with the following command:{" "}
|
||||
<code className="interactive-tutorial-command">scan-analyze 2</code>.
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalConnect as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
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 <code className="interactive-tutorial-command">connect [hostname]</code> command.
|
||||
<br />
|
||||
<br />
|
||||
From the results of the <code className="interactive-tutorial-command">scan-analyze</code> command, we can see
|
||||
that the <code className="interactive-tutorial-command">n00dles</code> server is only one node away. Let's
|
||||
connect so it now using: <code className="interactive-tutorial-command">connect n00dles</code>
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalAnalyze as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
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{" "}
|
||||
<code className="interactive-tutorial-command">analyze</code> command.
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalNuke as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
When the <code className="interactive-tutorial-command">analyze</code> command finishes running it will show
|
||||
useful information about hacking the server. <br />
|
||||
<br /> For this server, the required hacking skill is only <span className="character-hack-cell">1</span>, which
|
||||
means you can hack it right now. However, in order to hack a server you must first gain root access. The{" "}
|
||||
<code className="interactive-tutorial-command">NUKE.exe</code> 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 <code className="interactive-tutorial-command">analyze</code> 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{" "}
|
||||
<code className="interactive-tutorial-command">run NUKE.exe</code> command.
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.TerminalManualHack as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
You now have root access! You can hack the server using the{" "}
|
||||
<code className="interactive-tutorial-command">hack</code> command. Try doing that now.
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.TerminalHackingMechanics as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
You are now attempting to hack the server. Performing a hack takes time and only has a certain percentage chance
|
||||
of success. This time and success chance is determined by a variety of factors, including your hacking skill and
|
||||
the server's security level.
|
||||
<br />
|
||||
<br />
|
||||
If your attempt to hack the server is successful, you will steal a certain percentage of the server's total
|
||||
money. This percentage is affected by your hacking skill and the server's security level.
|
||||
<br />
|
||||
<br />
|
||||
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.
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.TerminalCreateScript as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
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{" "}
|
||||
<code className="interactive-tutorial-command">nano</code>
|
||||
command. Scripts must end with the <code className="interactive-tutorial-command">.script</code> extension.
|
||||
Let's make a script now by entering <code className="interactive-tutorial-command">nano n00dles.script</code>{" "}
|
||||
after the hack command finishes running (Sidenote: Pressing ctrl + c will end a command like hack early)
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalTypeScript as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
This is the script editor. You can use it to program your scripts. Scripts are written in a simplified version
|
||||
of javascript. Copy and paste the following code into the script editor: <br />
|
||||
</Typography>
|
||||
<pre className="interactive-tutorial-code">
|
||||
while(true) {"{"}
|
||||
hack('n00dles');
|
||||
{"}"}
|
||||
</pre>
|
||||
<Typography>
|
||||
For anyone with basic programming experience, this code should be straightforward. This script will
|
||||
continuously hack the <code className="interactive-tutorial-command">n00dles</code> server.
|
||||
<br />
|
||||
<br />
|
||||
To save and close the script editor, press the button in the bottom left, or press ctrl + b.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalFree as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
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{" "}
|
||||
<code className="interactive-tutorial-command">free</code> command.
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalRunScript as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
We have 4GB of free RAM on this machine, which is enough to run our script. Let's run our script using{" "}
|
||||
<code className="interactive-tutorial-command">run n00dles.script</code>.
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalGoToActiveScriptsPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
Your script is now running! It will continuously run in the background and will automatically stop if the code
|
||||
ever completes (the <code className="interactive-tutorial-command">n00dles.script</code> will never complete
|
||||
because it runs an infinite loop). <br />
|
||||
<br />
|
||||
These scripts can passively earn you income and hacking experience. Your scripts will also earn money and
|
||||
experience while you are offline, although at a slightly slower rate. <br />
|
||||
<br />
|
||||
Let's check out some statistics for our running scripts by clicking{" "}
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<StorageIcon color={"error"} />
|
||||
<Typography color={"error"}>Active Scripts</Typography>
|
||||
</ListItem>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.ActiveScriptsPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
This page displays information about all of your scripts that are running across every server. You can use
|
||||
this to gauge how well your scripts are doing. Let's go back to
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<EqualizerIcon color={"error"} />
|
||||
<Typography color={"error"}>Terminal</Typography>
|
||||
</ListItem>
|
||||
</>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.ActiveScriptsToTerminal as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
One last thing about scripts, each active script contains logs that detail what it's doing. We can check these
|
||||
logs using the <code className="interactive-tutorial-command">tail</code> command. Do that now for the script we
|
||||
just ran by typing <code className="interactive-tutorial-command">tail n00dles.script</code>
|
||||
</Typography>
|
||||
),
|
||||
canNext: false,
|
||||
},
|
||||
[iTutorialSteps.TerminalTailScript as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
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 covers the basics of hacking. To learn more about writing scripts, select
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<HelpIcon color={"primary"} />
|
||||
<Typography color={"primary"}>Tutorial</Typography>
|
||||
</ListItem>
|
||||
<Typography>
|
||||
in the main navigation menu to look at the documentation.
|
||||
<strong style={{ backgroundColor: "#444" }}>
|
||||
If you are an experienced JavaScript developer, I would highly suggest you check out the section on
|
||||
NetscriptJS/Netscript 2.0, it's faster and more powerful.
|
||||
</strong>
|
||||
<br />
|
||||
<br />
|
||||
For now, let's move on to something else!
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.GoToHacknetNodesPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
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
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<AccountTreeIcon color={"error"} />
|
||||
<Typography color={"error"}>Hacknet</Typography>
|
||||
</ListItem>
|
||||
<Typography>through the main navigation menu now.</Typography>
|
||||
</>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.HacknetNodesIntroduction as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
here you can purchase new Hacknet Nodes and upgrade your existing ones. Let's purchase a new one now.
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.HacknetNodesGoToWorldPage as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
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.
|
||||
<br />
|
||||
<br />
|
||||
Let's go to
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<LocationCityIcon color={"error"} />
|
||||
<Typography color={"error"}>City</Typography>
|
||||
</ListItem>
|
||||
</>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.WorldDescription as number]: {
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
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!
|
||||
<br />
|
||||
<br />
|
||||
Lastly, click on
|
||||
</Typography>
|
||||
<ListItem>
|
||||
<HelpIcon color={"error"} />
|
||||
<Typography color={"error"}>Tutorial</Typography>
|
||||
</ListItem>
|
||||
</>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.TutorialPageInfo as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
This page contains a lot of different documentation about the game's content and mechanics.{" "}
|
||||
<strong style={{ backgroundColor: "#444" }}>
|
||||
{" "}
|
||||
I know it's a lot, but I highly suggest you read (or at least skim) through this before you start playing
|
||||
</strong>
|
||||
. That's the end of the tutorial. Hope you enjoy the game!
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
},
|
||||
[iTutorialSteps.End as number]: {
|
||||
content: <Typography></Typography>,
|
||||
canNext: true,
|
||||
},
|
||||
};
|
||||
|
||||
export function InteractiveTutorialRoot(): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return ITutorialEvents.subscribe(rerender);
|
||||
}, []);
|
||||
const step = ITutorial.currStep;
|
||||
const content = contents[step];
|
||||
if (content === undefined) throw new Error("error in the tutorial");
|
||||
return (
|
||||
<Paper square sx={{ maxWidth: "35vh", p: 2 }}>
|
||||
{content.content}
|
||||
<IconButton onClick={iTutorialPrevStep}>
|
||||
<ArrowBackIos />
|
||||
</IconButton>
|
||||
{content.canNext && (
|
||||
<IconButton onClick={iTutorialNextStep}>
|
||||
<ArrowForwardIos />
|
||||
</IconButton>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Button onClick={iTutorialEnd}>EXIT</Button>
|
||||
</Paper>
|
||||
);
|
||||
}
|
@ -21,8 +21,9 @@ import SaveAltIcon from "@mui/icons-material/SaveAlt";
|
||||
|
||||
import { colors } from "./Theme";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Page } from "../../ui/Router";
|
||||
import { use } from "../Context";
|
||||
import { Page } from "../Router";
|
||||
import { Overview } from "./Overview";
|
||||
|
||||
interface IProps {
|
||||
save: () => void;
|
||||
@ -105,9 +106,6 @@ const useStyles = makeStyles({
|
||||
int: {
|
||||
color: colors.int,
|
||||
},
|
||||
nobackground: {
|
||||
backgroundColor: "#0000",
|
||||
},
|
||||
});
|
||||
|
||||
export function CharacterOverview({ save }: IProps): React.ReactElement {
|
||||
@ -115,9 +113,7 @@ export function CharacterOverview({ save }: IProps): React.ReactElement {
|
||||
const router = use.Router();
|
||||
|
||||
if (router.page() === Page.BitVerse) return <></>;
|
||||
|
||||
const setRerender = useState(false)[1];
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setRerender((old) => !old), 600);
|
||||
@ -126,120 +122,103 @@ export function CharacterOverview({ save }: IProps): React.ReactElement {
|
||||
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div style={{ position: "fixed", top: 0, right: 0, zIndex: 1500 }}>
|
||||
<Box display="flex" justifyContent="flex-end" flexDirection={"column"}>
|
||||
<Collapse in={open}>
|
||||
<Paper square>
|
||||
<Box m={1}>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>HP </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>
|
||||
{numeralWrapper.formatHp(player.hp)} / {numeralWrapper.formatHp(player.max_hp)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Paper square>
|
||||
<Box m={1}>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>HP </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>
|
||||
{numeralWrapper.formatHp(player.hp)} / {numeralWrapper.formatHp(player.max_hp)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>Money </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>
|
||||
{numeralWrapper.formatMoney(player.money.toNumber())}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>Money </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>
|
||||
{numeralWrapper.formatMoney(player.money.toNumber())}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>Hack </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>
|
||||
{numeralWrapper.formatSkill(player.hacking_skill)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>Hack </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>
|
||||
{numeralWrapper.formatSkill(player.hacking_skill)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Str </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.strength)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Str </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.strength)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Def </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.defense)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Def </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.defense)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Dex </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.dexterity)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>Agi </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.agility)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Dex </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.dexterity)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>Agi </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.agility)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>Cha </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>
|
||||
{numeralWrapper.formatSkill(player.charisma)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Intelligence />
|
||||
<Work />
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>Cha </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>{numeralWrapper.formatSkill(player.charisma)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Intelligence />
|
||||
<Work />
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
||||
<IconButton onClick={save}>
|
||||
<SaveAltIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Collapse>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Fab classes={{ root: classes.nobackground }} onClick={() => setOpen((old) => !old)}>
|
||||
<VisibilityOffIcon color="primary" />
|
||||
</Fab>
|
||||
</Box>
|
||||
<TableRow>
|
||||
<TableCell align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
||||
<IconButton onClick={save}>
|
||||
<SaveAltIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</div>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
justifyContent: "center",
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: "2px solid " + theme.palette.primary.main,
|
||||
boxShadow: theme.shadows[5],
|
||||
boxShadow: `0px 3px 5px -1px ${theme.palette.primary.dark},0px 5px 8px 0px ${theme.palette.primary.dark},0px 1px 14px 0px ${theme.palette.primary.dark}`,
|
||||
padding: 2,
|
||||
maxWidth: "80%",
|
||||
maxHeight: "80%",
|
||||
|
34
src/ui/React/Overview.tsx
Normal file
34
src/ui/React/Overview.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import Box from "@mui/material/Box";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import Fab from "@mui/material/Fab";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
nobackground: {
|
||||
backgroundColor: "#0000",
|
||||
},
|
||||
});
|
||||
|
||||
interface IProps {
|
||||
children: JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement;
|
||||
}
|
||||
|
||||
export function Overview({ children }: IProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(true);
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div style={{ position: "fixed", top: 0, right: 0, zIndex: 1500 }}>
|
||||
<Box display="flex" justifyContent="flex-end" flexDirection={"column"}>
|
||||
<Collapse in={open}>{children}</Collapse>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Fab classes={{ root: classes.nobackground }} onClick={() => setOpen((old) => !old)}>
|
||||
<VisibilityOffIcon color="primary" />
|
||||
</Fab>
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user