mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-10 09:43:54 +01:00
Merge pull request #3586 from danielyxie/better-ns-tutorial
MISC: Make tutorial explain ns1 vs ns2 better
This commit is contained in:
commit
9f18e114d6
@ -7,6 +7,7 @@ import { ITutorialEvents } from "./ui/InteractiveTutorial/ITutorialEvents";
|
|||||||
// Ordered array of keys to Interactive Tutorial Steps
|
// Ordered array of keys to Interactive Tutorial Steps
|
||||||
enum iTutorialSteps {
|
enum iTutorialSteps {
|
||||||
Start,
|
Start,
|
||||||
|
NSSelection,
|
||||||
GoToCharacterPage, // Click on 'Stats' page
|
GoToCharacterPage, // Click on 'Stats' page
|
||||||
CharacterPage, // Introduction to 'Stats' page
|
CharacterPage, // Introduction to 'Stats' page
|
||||||
CharacterGoToTerminalPage, // Go back to Terminal
|
CharacterGoToTerminalPage, // Go back to Terminal
|
||||||
@ -43,6 +44,7 @@ const ITutorial: {
|
|||||||
isRunning: boolean;
|
isRunning: boolean;
|
||||||
stepIsDone: {
|
stepIsDone: {
|
||||||
[iTutorialSteps.Start]: boolean;
|
[iTutorialSteps.Start]: boolean;
|
||||||
|
[iTutorialSteps.NSSelection]: boolean;
|
||||||
[iTutorialSteps.GoToCharacterPage]: boolean;
|
[iTutorialSteps.GoToCharacterPage]: boolean;
|
||||||
[iTutorialSteps.CharacterPage]: boolean;
|
[iTutorialSteps.CharacterPage]: boolean;
|
||||||
[iTutorialSteps.CharacterGoToTerminalPage]: boolean;
|
[iTutorialSteps.CharacterGoToTerminalPage]: boolean;
|
||||||
@ -80,6 +82,7 @@ const ITutorial: {
|
|||||||
// Keeps track of whether each step has been done
|
// Keeps track of whether each step has been done
|
||||||
stepIsDone: {
|
stepIsDone: {
|
||||||
[iTutorialSteps.Start]: false,
|
[iTutorialSteps.Start]: false,
|
||||||
|
[iTutorialSteps.NSSelection]: false,
|
||||||
[iTutorialSteps.GoToCharacterPage]: false,
|
[iTutorialSteps.GoToCharacterPage]: false,
|
||||||
[iTutorialSteps.CharacterPage]: false,
|
[iTutorialSteps.CharacterPage]: false,
|
||||||
[iTutorialSteps.CharacterGoToTerminalPage]: false,
|
[iTutorialSteps.CharacterGoToTerminalPage]: false,
|
||||||
|
@ -544,11 +544,14 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
// this is duplicate code with saving later.
|
// this is duplicate code with saving later.
|
||||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||||
//Make sure filename + code properly follow tutorial
|
//Make sure filename + code properly follow tutorial
|
||||||
if (currentScript.fileName !== "n00dles.script") {
|
if (currentScript.fileName !== "n00dles.script" && currentScript.fileName !== "n00dles.js") {
|
||||||
dialogBoxCreate("Leave the script name as 'n00dles.script'!");
|
dialogBoxCreate("Don't change the script name for now.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentScript.code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
|
const cleanCode = currentScript.code.replace(/\s/g, "");
|
||||||
|
const ns1 = "while(true){hack('n00dles');}";
|
||||||
|
const ns2 = `exportasyncfunctionmain(ns){while(true){awaitns.hack('n00dles');}}`;
|
||||||
|
if (cleanCode.indexOf(ns1) == -1 && cleanCode.indexOf(ns2) == -1) {
|
||||||
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -718,7 +718,11 @@ export class Terminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalCreateScript:
|
case iTutorialSteps.TerminalCreateScript:
|
||||||
if (commandArray.length == 2 && commandArray[0] == "nano" && commandArray[1] == "n00dles.script") {
|
if (
|
||||||
|
commandArray.length == 2 &&
|
||||||
|
commandArray[0] == "nano" &&
|
||||||
|
(commandArray[1] == "n00dles.script" || commandArray[1] == "n00dles.js")
|
||||||
|
) {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
} else {
|
} else {
|
||||||
this.error("Bad command. Please follow the tutorial");
|
this.error("Bad command. Please follow the tutorial");
|
||||||
@ -734,7 +738,11 @@ export class Terminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalRunScript:
|
case iTutorialSteps.TerminalRunScript:
|
||||||
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "n00dles.script") {
|
if (
|
||||||
|
commandArray.length == 2 &&
|
||||||
|
commandArray[0] == "run" &&
|
||||||
|
(commandArray[1] == "n00dles.script" || commandArray[1] == "n00dles.js")
|
||||||
|
) {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
} else {
|
} else {
|
||||||
this.error("Bad command. Please follow the tutorial");
|
this.error("Bad command. Please follow the tutorial");
|
||||||
@ -742,7 +750,11 @@ export class Terminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.ActiveScriptsToTerminal:
|
case iTutorialSteps.ActiveScriptsToTerminal:
|
||||||
if (commandArray.length == 2 && commandArray[0] == "tail" && commandArray[1] == "n00dles.script") {
|
if (
|
||||||
|
commandArray.length == 2 &&
|
||||||
|
commandArray[0] == "tail" &&
|
||||||
|
(commandArray[1] == "n00dles.script" || commandArray[1] == "n00dles.js")
|
||||||
|
) {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
} else {
|
} else {
|
||||||
this.error("Bad command. Please follow the tutorial");
|
this.error("Bad command. Please follow the tutorial");
|
||||||
|
@ -9,6 +9,7 @@ import ArrowBackIos from "@mui/icons-material/ArrowBackIos";
|
|||||||
import { ITutorialEvents } from "./ITutorialEvents";
|
import { ITutorialEvents } from "./ITutorialEvents";
|
||||||
import { CopyableText } from "../React/CopyableText";
|
import { CopyableText } from "../React/CopyableText";
|
||||||
|
|
||||||
|
import List from "@mui/material/List";
|
||||||
import ListItem from "@mui/material/ListItem";
|
import ListItem from "@mui/material/ListItem";
|
||||||
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
||||||
import LastPageIcon from "@mui/icons-material/LastPage";
|
import LastPageIcon from "@mui/icons-material/LastPage";
|
||||||
@ -27,6 +28,7 @@ import {
|
|||||||
iTutorialSteps,
|
iTutorialSteps,
|
||||||
iTutorialEnd,
|
iTutorialEnd,
|
||||||
} from "../../InteractiveTutorial";
|
} from "../../InteractiveTutorial";
|
||||||
|
import { NSSelection } from "./NSSelection";
|
||||||
|
|
||||||
interface IContent {
|
interface IContent {
|
||||||
content: React.ReactElement;
|
content: React.ReactElement;
|
||||||
@ -45,9 +47,23 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
enum Language {
|
||||||
|
None,
|
||||||
|
NS1,
|
||||||
|
NS2,
|
||||||
|
}
|
||||||
|
|
||||||
export function InteractiveTutorialRoot(): React.ReactElement {
|
export function InteractiveTutorialRoot(): React.ReactElement {
|
||||||
|
const [nsSelectionOpen, setNSSelectionOpen] = useState(false);
|
||||||
|
const [language, setLanguage] = useState(Language.None);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const tutorialScriptName = {
|
||||||
|
[Language.None]: "n00dles.script",
|
||||||
|
[Language.NS1]: "n00dles.script",
|
||||||
|
[Language.NS2]: "n00dles.js",
|
||||||
|
}[language];
|
||||||
|
|
||||||
const contents: { [number: string]: IContent | undefined } = {
|
const contents: { [number: string]: IContent | undefined } = {
|
||||||
[iTutorialSteps.Start as number]: {
|
[iTutorialSteps.Start as number]: {
|
||||||
content: (
|
content: (
|
||||||
@ -66,6 +82,47 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
),
|
),
|
||||||
canNext: true,
|
canNext: true,
|
||||||
},
|
},
|
||||||
|
[iTutorialSteps.NSSelection as number]: {
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
<Typography>The tutorial will adjust to your programming ability.</Typography>
|
||||||
|
<Typography>Bitburner has 2 types of scripts:</Typography>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<Typography>NS1: Javascript from 2009, very simple. Recommended for beginners to programming.</Typography>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Typography>
|
||||||
|
NS2: Native, modern Javascript. Recommended if you know any programming language or are serious about
|
||||||
|
learning programming.
|
||||||
|
</Typography>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
<Typography>
|
||||||
|
Both are available at all time and interchangeably. This choice is only for the tutorial.
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setLanguage(Language.NS1);
|
||||||
|
iTutorialNextStep();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use NS1
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setLanguage(Language.NS2);
|
||||||
|
iTutorialNextStep();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use NS2
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setNSSelectionOpen(true)}>More info</Button>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
canNext: false,
|
||||||
|
},
|
||||||
[iTutorialSteps.GoToCharacterPage as number]: {
|
[iTutorialSteps.GoToCharacterPage as number]: {
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
@ -321,7 +378,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano"}</Typography>
|
||||||
|
|
||||||
<Typography>Scripts must end with the .script extension. Let's make a script now by entering </Typography>
|
<Typography>Scripts must end with the .script extension. Let's make a script now by entering </Typography>
|
||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano n00dles.script"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> nano ${tutorialScriptName}`}</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
after the hack command finishes running (Sidenote: Pressing ctrl + c will end a command like hack early)
|
after the hack command finishes running (Sidenote: Pressing ctrl + c will end a command like hack early)
|
||||||
@ -334,16 +391,28 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<Typography>
|
<Typography>
|
||||||
This is the script editor. You can use it to program your scripts. Scripts are written in a simplified
|
This is the script editor. You can use it to program your scripts.{" "}
|
||||||
version of javascript. Copy and paste the following code into the script editor: <br />
|
{language !== Language.NS2 && <>Scripts are written in a simplified version of javascript.</>} Copy and
|
||||||
|
paste the following code into the script editor: <br />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography classes={{ root: classes.code }}>
|
<Typography classes={{ root: classes.code }}>
|
||||||
<CopyableText
|
{language !== Language.NS2 && (
|
||||||
value={`while(true) {
|
<CopyableText
|
||||||
|
value={`while(true) {
|
||||||
hack('n00dles');
|
hack('n00dles');
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{language === Language.NS2 && (
|
||||||
|
<CopyableText
|
||||||
|
value={`export async function main(ns) {
|
||||||
|
while(true) {
|
||||||
|
await ns.hack('n00dles');
|
||||||
|
}
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
For anyone with basic programming experience, this code should be straightforward. This script will
|
For anyone with basic programming experience, this code should be straightforward. This script will
|
||||||
@ -378,7 +447,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
<Typography>
|
<Typography>
|
||||||
We have 8GB of free RAM on this machine, which is enough to run our script. Let's run our script using
|
We have 8GB of free RAM on this machine, which is enough to run our script. Let's run our script using
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> run n00dles.script"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> run ${tutorialScriptName}`}</Typography>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
canNext: false,
|
canNext: false,
|
||||||
@ -425,7 +494,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
One last thing about scripts, each active script contains logs that detail what it's doing. We can check
|
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{" "}
|
these logs using the tail command. Do that now for the script we just ran by typing{" "}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> tail n00dles.script"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> tail ${tutorialScriptName}`}</Typography>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
canNext: false,
|
canNext: false,
|
||||||
@ -447,14 +516,6 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
in the main navigation menu to look at the documentation.
|
in the main navigation menu to look at the documentation.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
If you know even a little bit of programming it is highly recommended you use NS2 instead. You will enjoy
|
|
||||||
the game much more. NS1 files end with .script and are a subset of javascript. NS2 files end with .js and
|
|
||||||
are full speed native javascript.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
You can learn more about the difference between them later in the documentation.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
For now, let's move on to something else!
|
For now, let's move on to something else!
|
||||||
</Typography>
|
</Typography>
|
||||||
</>
|
</>
|
||||||
@ -549,25 +610,30 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
const content = contents[step];
|
const content = contents[step];
|
||||||
if (content === undefined) throw new Error("error in the tutorial");
|
if (content === undefined) throw new Error("error in the tutorial");
|
||||||
return (
|
return (
|
||||||
<Paper square sx={{ maxWidth: "70vw", p: 2 }}>
|
<>
|
||||||
{content.content}
|
<NSSelection open={nsSelectionOpen} onClose={() => setNSSelectionOpen(false)} />
|
||||||
{step !== iTutorialSteps.TutorialPageInfo && (
|
<Paper square sx={{ maxWidth: "70vw", p: 2 }}>
|
||||||
<>
|
{content.content}
|
||||||
<IconButton onClick={iTutorialPrevStep} aria-label="previous">
|
{step !== iTutorialSteps.TutorialPageInfo && (
|
||||||
<ArrowBackIos />
|
<>
|
||||||
</IconButton>
|
{step !== iTutorialSteps.Start && (
|
||||||
{content.canNext && (
|
<IconButton onClick={iTutorialPrevStep} aria-label="previous">
|
||||||
<IconButton onClick={iTutorialNextStep} aria-label="next">
|
<ArrowBackIos />
|
||||||
<ArrowForwardIos />
|
</IconButton>
|
||||||
</IconButton>
|
)}
|
||||||
)}
|
{content.canNext && (
|
||||||
</>
|
<IconButton onClick={iTutorialNextStep} aria-label="next">
|
||||||
)}
|
<ArrowForwardIos />
|
||||||
<br />
|
</IconButton>
|
||||||
<br />
|
)}
|
||||||
<Button onClick={iTutorialEnd}>
|
</>
|
||||||
{step !== iTutorialSteps.TutorialPageInfo ? "SKIP TUTORIAL" : "FINISH TUTORIAL"}
|
)}
|
||||||
</Button>
|
<br />
|
||||||
</Paper>
|
<br />
|
||||||
|
<Button onClick={iTutorialEnd}>
|
||||||
|
{step !== iTutorialSteps.TutorialPageInfo ? "SKIP TUTORIAL" : "FINISH TUTORIAL"}
|
||||||
|
</Button>
|
||||||
|
</Paper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
82
src/ui/InteractiveTutorial/NSSelection.tsx
Normal file
82
src/ui/InteractiveTutorial/NSSelection.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import Editor from "@monaco-editor/react";
|
||||||
|
import { Tab, Tabs, Typography } from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Modal } from "../React/Modal";
|
||||||
|
|
||||||
|
import * as monaco from "monaco-editor";
|
||||||
|
|
||||||
|
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ns1Example = `while(true) {
|
||||||
|
hack('n00dles');
|
||||||
|
}`;
|
||||||
|
const ns2Example = `/** @param {NS} ns */
|
||||||
|
export async function main(ns) {
|
||||||
|
while(true) {
|
||||||
|
await ns.hack('n00dles');
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export function NSSelection(props: IProps): React.ReactElement {
|
||||||
|
const [value, setValue] = React.useState(0);
|
||||||
|
|
||||||
|
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||||
|
setValue(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMount(editor: IStandaloneCodeEditor): void {
|
||||||
|
editor.updateOptions({ readOnly: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||||
|
<Tab label="NS1" />
|
||||||
|
<Tab label="NS2" />
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
{value === 0 && (
|
||||||
|
<>
|
||||||
|
<Typography>
|
||||||
|
These scripts end with '.script'. Using a very old interpreted version of javascript. It is perfect for
|
||||||
|
beginner to programming.
|
||||||
|
</Typography>
|
||||||
|
<Typography>Example script using NS1:</Typography>
|
||||||
|
<Editor
|
||||||
|
loading={<></>}
|
||||||
|
defaultLanguage="javascript"
|
||||||
|
defaultValue={ns1Example}
|
||||||
|
height={`${300}px`}
|
||||||
|
theme={"vs-dark"}
|
||||||
|
onMount={onMount}
|
||||||
|
options={{ fontSize: 30 }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{value === 1 && (
|
||||||
|
<>
|
||||||
|
<Typography>
|
||||||
|
These scripts end with '.js'. Scripts using ns2 are running natively along the game. If you know any
|
||||||
|
programming language you should be using this. However if you're unfamiliar with javascript Promises you
|
||||||
|
might want to read up on them a little bit before diving in.
|
||||||
|
</Typography>
|
||||||
|
<Typography>Example script using NS2:</Typography>
|
||||||
|
<Editor
|
||||||
|
loading={<></>}
|
||||||
|
defaultLanguage="javascript"
|
||||||
|
defaultValue={ns2Example}
|
||||||
|
height={`${300}px`}
|
||||||
|
theme={"vs-dark"}
|
||||||
|
options={{ fontSize: 30 }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -12,6 +12,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
zIndex: 999999,
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
|
Loading…
Reference in New Issue
Block a user